Forms & Server Actions
⏱ 18 min read read
Server Actions --- Forms Without API Routes:
Server Actions let you run server-side code directly from a form submit
--- no API route needed. Mark a function with 'use server'.
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createStudent(formData: FormData) {
const name = formData.get('name') as string;
const grade = Number(formData.get('grade'));
// Save to DB here...
await db.students.create({ name, grade });
revalidatePath('/students'); // refresh the page data
}
// app/students/page.tsx --- use directly in form
import { createStudent } from '@/app/actions';
export default function Page() {
return (
<form action={createStudent}>
<input name="name" placeholder="Name" />
<input name="grade" type="number" />
<button type="submit">Add Student</button>
</form>
);
}
React Hook Form + Zod --- Client-Side Validation:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, 'Min 2 characters'),
email: z.string().email('Invalid email'),
grade: z.number().min(0).max(100),
});
type FormData = z.infer<typeof schema>;
const { register, handleSubmit, formState: { errors } } =
useForm<FormData>({
resolver: zodResolver(schema),
});
Server Actions are the Next.js 13+ way to handle mutations.
They work without JavaScript in the browser (progressive
enhancement).
Use useFormState and useFormStatus for pending/error feedback.
React Hook Form + Zod = best client-side form experience.
Install: npm i react-hook-form zod @hookform/resolvers
Server Actions let you run server-side code directly from a form submit
--- no API route needed. Mark a function with 'use server'.
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createStudent(formData: FormData) {
const name = formData.get('name') as string;
const grade = Number(formData.get('grade'));
// Save to DB here...
await db.students.create({ name, grade });
revalidatePath('/students'); // refresh the page data
}
// app/students/page.tsx --- use directly in form
import { createStudent } from '@/app/actions';
export default function Page() {
return (
<form action={createStudent}>
<input name="name" placeholder="Name" />
<input name="grade" type="number" />
<button type="submit">Add Student</button>
</form>
);
}
React Hook Form + Zod --- Client-Side Validation:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, 'Min 2 characters'),
email: z.string().email('Invalid email'),
grade: z.number().min(0).max(100),
});
type FormData = z.infer<typeof schema>;
const { register, handleSubmit, formState: { errors } } =
useForm<FormData>({
resolver: zodResolver(schema),
});
Server Actions are the Next.js 13+ way to handle mutations.
They work without JavaScript in the browser (progressive
enhancement).
Use useFormState and useFormStatus for pending/error feedback.
React Hook Form + Zod = best client-side form experience.
Install: npm i react-hook-form zod @hookform/resolvers
Log in to track your progress and earn badges as you complete lessons.
Log In to Track Progress