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});
9
10// action.ts
11'use server';
12
13import { redirect } from 'next/navigation';
14import { parseWithZod } from '@conform-to/zod';
15import { loginSchema } from '@/app/schema';
16
17export async function login(prevState: unknown, formData: FormData) {
18 const submission = parseWithZod(formData, {
19 schema: loginSchema,
20 });
21
22 if (submission.status !== 'success') {
23 return submission.reply();
24 }
25
26 redirect('/dashboard');
27}
28
29// form.tsx
30'use client';
31
32import { useForm } from '@conform-to/react';
33import { parseWithZod } from '@conform-to/zod';
34import { useFormState } from 'react-dom';
35import { login } from '@/app/actions';
36import { loginSchema } from '@/app/schema';
37
38export function LoginForm() {
39 const [lastResult, action] = useFormState(login, undefined);
40 const [form, fields] = useForm({
41 // Sync the result of last submission
42 lastResult,
43
44 // Reuse the validation logic on the client
45 onValidate({ formData }) {
46 return parseWithZod(formData, { schema: loginSchema });
47 },
48
49 // Validate the form on blur event triggered
50 shouldValidate: 'onBlur',
51 });
52
53 return (
54 <form id={form.id} onSubmit={form.onSubmit} action={action} noValidate>
55 <div>
56 <label>Email</label>
57 <input type="email" name={fields.email.name} />
58 <div>{fields.email.errors}</div>
59 </div>
60 <div>
61 <label>Password</label>
62 <input type="password" name={fields.password.name} />
63 <div>{fields.password.errors}</div>
64 </div>
65 <label>
66 <div>
67 <span>Remember me</span>
68 <input type="checkbox" name={fields.remember.name} />
69 </div>
70 </label>
71 <Button>Login</Button>
72 </form>
73 );
74}