Next.js
Here is a login form example integrating with Next.js. You can find the full example here.
1// schema.ts
2import { z } from 'zod';
3
4export const loginSchema = z.object({
5 email: z.string().email(),
6 password: z.string(),
7 remember: z.boolean().optional(),
8});
1'use server'; // action.ts
2
3import { redirect } from 'next/navigation';
4import { parseWithZod } from '@conform-to/zod';
5import { loginSchema } from '@/app/schema';
6
7export async function login(prevState: unknown, formData: FormData) {
8 const submission = parseWithZod(formData, {
9 schema: loginSchema,
10 });
11
12 if (submission.status !== 'success') {
13 return submission.reply();
14 }
15
16 redirect('/dashboard');
17}
1'use client'; // form.tsx
2
3import { useForm } from '@conform-to/react';
4import { parseWithZod } from '@conform-to/zod';
5import { useFormState } from 'react-dom';
6import { login } from '@/app/actions';
7import { loginSchema } from '@/app/schema';
8
9export function LoginForm() {
10 const [lastResult, action] = useFormState(login, undefined);
11 const [form, fields] = useForm({
12 // Sync the result of last submission
13 lastResult,
14
15 // Reuse the validation logic on the client
16 onValidate({ formData }) {
17 return parseWithZod(formData, { schema: loginSchema });
18 },
19
20 // Validate the form on blur event triggered
21 shouldValidate: 'onBlur',
22 shouldRevalidate: 'onInput',
23 });
24
25 return (
26 <form id={form.id} onSubmit={form.onSubmit} action={action} noValidate>
27 <div>
28 <label>Email</label>
29 <input
30 type="email"
31 key={fields.email.key}
32 name={fields.email.name}
33 defaultValue={fields.email.initialValue}
34 />
35 <div>{fields.email.errors}</div>
36 </div>
37 <div>
38 <label>Password</label>
39 <input
40 type="password"
41 key={fields.password.key}
42 name={fields.password.name}
43 defaultValue={fields.password.initialValue}
44 />
45 <div>{fields.password.errors}</div>
46 </div>
47 <label>
48 <div>
49 <span>Remember me</span>
50 <input
51 type="checkbox"
52 key={fields.remember.key}
53 name={fields.remember.name}
54 defaultChecked={fields.remember.initialValue === 'on'}
55 />
56 </div>
57 </label>
58 <button>Login</button>
59 </form>
60 );
61}