Integration / Next.js

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}