# Persist inputs after a form submit in Remix

Let's say you have a form with a few inputs. You want to persist the inputs' values after submitting a form in case of an error.

The simplest way is to use the `<Form>` component because that will use `fetch` to submit the form data to the action, then return the error.

```tsx
export async function action({ request }: ActionArgs) {
  let formData = await request.formData();
  // do something with formData
  return json({ error: "Something failed" }, 400);
}

export default function Route() {
  let actionData = useActionData<typeof action>();

  return (
    <Form method="post">
      {actionData?.error ? <p>{actionData.error}</p> : null}
      <input name="value" type="text" />
      <button type="submit">Submit</button>
    </Form>
  );
}
```

Because we don't reload the page unless we return a redirect response or throw any response, causing the CatchBoundary to render, it will leave the inputs with the values they had before the submission.

But let's say we want to support a no-JS environment. In this scenario, the whole page will reload after the submission, so we need to persist the values of the inputs. We can do this in two ways.

## Return them from the action

The simplest way, we have the value on the FormData object so we can add them to the response, then in the UI, get the values from the `useActionData` hook and use them as defaultValue on the input.

```tsx
export async function action({ request }: ActionArgs) {
  let formData = await request.formData();
  // do something with formData
  return json(
    { error: "Something failed", fields: { value: formData.get("value") } },
    400
  );
}

export default function Route() {
  let actionData = useActionData<typeof action>();

  return (
    <Form method="post">
      {actionData?.error ? <p>{actionData.error}</p> : null}
      <input name="value" type="text" defaultValue={actionData?.fields.value} />
      <button type="submit">Submit</button>
    </Form>
  );
}
```

Another option here is to use the value from the `useActionData` hook as the initial value of a State.

```tsx
export default function Route() {
  let actionData = useActionData<typeof action>();

  let [value, setValue] = useState(() => {
    if (actionData?.fields.value) {
      return actionData.fields.value;
    }
    return "";
  });

  return (
    <Form method="post">
      {actionData?.error ? <p>{actionData.error}</p> : null}
      <input
        name="value"
        type="text"
        value={value}
        onChange={(event) => setValue(event.currentTarget.value)}
      />
      <button type="submit">Submit</button>
    </Form>
  );
}
```

It works better if you already have a State for your input for any reason. The page will reload if there's no JS, and the State will be initialized with the `actionData.fields.value`. Otherwise, the current step will remain untouched with the same value.

## Use session.flash

The second way, which requires more work, is to use `session.flash` to persist the values of the inputs.

```tsx
export async function action({ request }: ActionArgs) {
  let session = await sessionStorage.getSession(request.headers.get("Cookie"));
  let formData = await request.formData();

  // do something with formData

  session.flash("error", "Something failed");
  session.flash("fields", { value: formData.get("value") });
  return redirect("/route", {
    headers: { "Set-Cookie": await sessionStorage.commitSession(session) },
  });
}

export async function loader({ request }: LoaderArgs) {
  let session = await sessionStorage.getSession(request.headers.get("Cookie"));
  return json(
    { error: session.get("error"), fields: session.get("fields") ?? "" },
    { headers: { "Set-Cookie": await sessionStorage.commitSession(session) } }
  );
}

export default function Route() {
  let loaderData = useLoaderData<typeof loader>();

  return (
    <Form method="post">
      {loaderData.error ? <p>{loaderData.error}</p> : null}
      <input name="value" type="text" defaultValue={loaderData.fields.value} />
      <button type="submit">Submit</button>
    </Form>
  );
}
```

And as when returning from the error, you can use the `loaderData.fields.value` as the initial value of a State.