Remix
Here is a login form example integrating with Remix. You can find the full example here.
1import { getFormProps, getInputProps, useForm } from '@conform-to/react';
2import { parseWithZod } from '@conform-to/zod';
3import type { ActionArgs } from '@remix-run/node';
4import { json, redirect } from '@remix-run/node';
5import { Form, useActionData } from '@remix-run/react';
6import { z } from 'zod';
7
8const schema = z.object({
9 email: z.string().email(),
10 password: z.string(),
11 remember: z.boolean().optional(),
12});
13
14export async function action({ request }: ActionArgs) {
15 const formData = await request.formData();
16 const submission = parseWithZod(formData, { schema });
17
18 if (submission.status !== 'success') {
19 return json(submission.reply());
20 }
21
22 // ...
23}
24
25export default function Login() {
26 // Last submission returned by the server
27 const lastResult = useActionData<typeof action>();
28 const [form, fields] = useForm({
29 // Sync the result of last submission
30 lastResult,
31
32 // Reuse the validation logic on the client
33 onValidate({ formData }) {
34 return parseWithZod(formData, { schema });
35 },
36
37 // Validate the form on blur event triggered
38 shouldValidate: 'onBlur',
39 });
40
41 return (
42 <Form method="post" id={form.id} onSubmit={form.onSubmit}>
43 <div>
44 <label>Email</label>
45 <input type="email" name={fields.email.name} />
46 <div>{fields.email.errors}</div>
47 </div>
48 <div>
49 <label>Password</label>
50 <input type="password" name={fields.password.name} />
51 <div>{fields.password.errors}</div>
52 </div>
53 <label>
54 <div>
55 <span>Remember me</span>
56 <input type="checkbox" name={fields.remember.name} />
57 </div>
58 </label>
59 <hr />
60 <button>Login</button>
61 </Form>
62 );
63}