CONFORM

A progressive enhancement first form validation library for Remix and React Router
Highlights
- Progressive enhancement first APIs
- Automatic type coercion with Zod
- Simplifed integration through event delegation
- Field name inference
- Focus management
- Accessibility support
- About 5kb compressed
Quick Start
Here is an example built with Remix:
1import { useForm } from '@conform-to/react';
2import { parse } from '@conform-to/zod';
3import { Form, useActionData } from '@remix-run/react';
4import { json } from '@remix-run/node';
5import { z } from 'zod';
6import { authenticate } from '~/auth';
7
8const schema = z.object({
9 email: z
10 .string({ required_error: 'Email is required' })
11 .email('Email is invalid'),
12 password: z.string({ required_error: 'Password is required' }),
13});
14
15export async function action({ request }: ActionArgs) {
16 const formData = await request.formData();
17 const submission = parse(formData, { schema });
18
19 if (!submission.value || submission.intent !== 'submit') {
20 return json(submission);
21 }
22
23 return await authenticate(submission.value);
24}
25
26export default function LoginForm() {
27 const lastSubmission = useActionData<typeof action>();
28 const [form, { email, password }] = useForm({
29 lastSubmission,
30 onValidate({ formData }) {
31 return parse(formData, { schema });
32 },
33 });
34
35 return (
36 <Form method="post" {...form.props}>
37 <div>
38 <label>Email</label>
39 <input type="email" name={email.name} />
40 <div>{email.error}</div>
41 </div>
42 <div>
43 <label>Password</label>
44 <input type="password" name={password.name} />
45 <div>{password.error}</div>
46 </div>
47 <button>Login</button>
48 </Form>
49 );
50}
51