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});
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}