Getting Started / Overview

Overview

Conform is a type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.

#Features

  • Progressive enhancement first APIs
  • Type-safe field inference
  • Fine-grained subscription
  • Built-in accessibility helpers
  • Automatic type coercion with Zod

#The Gist

Conform gives you control over the form submission lifecycle from client to the server and exposes the form state through the useForm() hook. It does not restrict your form's markup and works with any valid HTML form. The form value will be captured from the DOM using the FormData Web API and is synced through event delegation.

1import { useForm } from '@conform-to/react';
2import { parseWithZod } from '@conform-to/zod';
3import { z } from 'zod';
4import { login } from './your-auth-library';
5import { useActionResult, redirect } from './your-server-framework';
6
7// Define a schema for your form
8const schema = z.object({
9  username: z.string(),
10  password: z.string(),
11});
12
13// Optional: Server action handler
14export async function action({ request }) {
15  const formData = await request.formData();
16  const submission = parseWithZod(formData, { schema });
17
18  // Send the submission back to the client if the status is not successful
19  if (submission.status !== 'success') {
20    return submission.reply();
21  }
22
23  const session = await login(submission.value);
24
25  // Send the submission with addional error message if login fails
26  if (!session) {
27    return submission.reply({
28      formErrors: ['Incorrect username or password'],
29    });
30  }
31
32  return redirect('/dashboard');
33}
34
35// Client form component
36export default function LoginForm() {
37  // Grab the last submission result if you have defined a server action handler
38  // This could be `useActionData()` or `useFormState()` depending on the framework
39  const lastResult = useActionResult();
40  const [form, fields] = useForm({
41    // Configure when each field should be validated
42    shouldValidate: 'onBlur',
43    // Optional: Required only if you're validating on the server
44    lastResult,
45    // Optional: Client validation. Fallback to server validation if not provided
46    onValidate({ formData }) {
47      return parseWithZod(formData, { schema });
48    },
49  });
50
51  return (
52    <form method="post" id={form.id} onSubmit={form.onSubmit}>
53      <div>{form.errors}</div>
54      <div>
55        <label>Username</label>
56        <input type="text" name={fields.username.name} />
57        <div>{fields.username.errors}</div>
58      </div>
59      <div>
60        <label>Password</label>
61        <input type="password" name={fields.password.name} />
62        <div>{fields.password.errors}</div>
63      </div>
64      <button>Login</button>
65    </form>
66  );
67}