# Fill with React (https://survey.dimah.dev/docs/react)



`useSurveyResponse()` reads a response, constructs a SurveyJS `Model` from its
stored definition, restores its data, and binds partial save and submit. Your
application still renders the model with `survey-react-ui`.

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm i @dimah-survey/react react survey-core survey-react-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add @dimah-survey/react react survey-core survey-react-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add @dimah-survey/react react survey-core survey-react-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add @dimah-survey/react react survey-core survey-react-ui
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout>
  `@dimah-survey/react` is a lifecycle binding, not a renderer wrapper. You
  retain direct access to the SurveyJS model and component.
</Callout>

```tsx title="components/fill.tsx"
"use client";

import dynamic from "next/dynamic";
import { createFillClient, useSurveyResponse } from "@dimah-survey/react";

import "survey-core/survey-core.min.css";

const Survey = dynamic(
  () => import("survey-react-ui").then((mod) => mod.Survey),
  { ssr: false },
);

const client = createFillClient({ baseURL: "/api/survey" });

export function Fill({ responseId }: { responseId: string }) {
  const { model, error, saveError, stale, reload } = useSurveyResponse({
    client,
    responseId,
  });

  if (error) return <p>{error.message}</p>;
  if (!model) return null;

  return (
    <>
      {saveError ? <p>{saveError.message}</p> : null}
      {stale ? <button onClick={reload}>Reload</button> : null}
      <Survey model={model} />
    </>
  );
}
```

SurveyJS UI components require a browser, so Next.js applications should load
`<Survey>` dynamically. A submitted or abandoned response opens in `display`
mode and sends no writes.

## Options [#options]

```ts
import type { UseSurveyResponseOptions } from "@dimah-survey/react";
```

<AutoTypeTable path="packages/react/src/use-survey-response.ts" name="UseSurveyResponseOptions" />

## Handle state [#handle-state]

```ts
import type { SurveyResponseBinding } from "@dimah-survey/react";
```

<AutoTypeTable path="packages/react/src/use-survey-response.ts" name="SurveyResponseBinding" />

Completion waits for the server. If submit fails, SurveyJS does not complete
and the respondent keeps their mounted model.

Each partial save sends the complete `survey.data` object; it is not a
key-level patch.

Writes are queued. Each one sends `expectedUpdatedAt` from the last successful
read. See [Responses](https://survey.dimah.dev/docs/responses.md).

## Bind an existing model [#bind-an-existing-model]

Use `bindSurveyModel()` when your application already owns the `Model`.
`useSurveyResponse()` is the load plus that binding.

```ts
import { bindSurveyModel } from "@dimah-survey/react";

const dispose = bindSurveyModel(model, {
  savePartial: (data) => client.savePartial({ id, data, expectedUpdatedAt }),
  submit: (data) => client.submitResponse({ id, data, expectedUpdatedAt }),
  onWriteError: (error) => {
    console.error(error);
  },
});
```

```ts
import type {
  SurveyModelActions,
  SurveyModelBindOptions,
} from "@dimah-survey/react";
```

<AutoTypeTable path="packages/react/src/bind-survey-model.ts" name="SurveyModelActions" />

<AutoTypeTable path="packages/react/src/bind-survey-model.ts" name="SurveyModelBindOptions" />

Call `dispose` on unmount. It removes the SurveyJS handlers it added.

`bindSurveyModel` also sets `storeDataAsText` to `false` on file and signature
questions, including questions added later.

## Keep file storage in your app [#keep-file-storage-in-your-app]

File bytes stay in your application. There is no upload route.

Handle `onUploadFiles`, `onDownloadFile`, and `onClearFiles` on the `Model`.
Give SurveyJS `{ file, content }` where `content` is your URL. Partial save
writes that locator into `data`. The binding does not store the bytes.

Start with the [Quickstart](https://survey.dimah.dev/docs/quickstart.md) for the complete load and render
flow. Creator autosave uses a separate binding described in
[Survey Creator](https://survey.dimah.dev/docs/creator.md).
