<form>.
This recipe walks through both halves of the write path: posting a new
comment, and editing an existing one.
Esix features used: create, save, find. Next.js features used: 'use server', FormData parsing, and revalidatePath.
What You’ll Build
A blog post page with a comment list and a form that appends a new comment. The form submits to a Server Action — nofetch, no JSON handlers, no
useState for the input.
The Comment Model
The Schema
Define what a valid comment looks like with zod — it doubles as the inferred TypeScript type. Addzod to the project first:
The Action
Server Actions are async functions marked with the'use server' directive.
Keep them in their own file so the boundary is unmistakable, and the action
can be imported by both server and client components:
- Validate before you write.
CommentSchema.parse(...)is one line and catches empty input, oversized payloads, and the wrong types in one go. Comment.createreturns the saved model. If you need the new id for a redirect, capture the return value.revalidatePathis what makes the new comment appear. Without it Next.js will keep serving the cached HTML it built before the comment existed.
Wiring the Form
Pass the action to the form’saction prop. The hidden postId field is the
simplest way to bind the action to the current post:
bind partially applies postId so the form only needs to carry the user’s
input. This is a server component, so the action arrives at the form already
wired — there’s no client-side fetch to write.
The Page
Render the comments and the form together. Both queries run on the server, inlined into the HTML on the way out:Editing an Existing Record
The pattern is the same — load, mutate, save. The update schema is a partial ofCommentSchema, and the action assigns each validated field onto the model
one at a time so a client can’t sneak in fields the form doesn’t expose:
Object.assign — makes the writable surface explicit. Adding a new editable
field means touching this file, which is exactly the kind of review-prompting
friction you want when expanding what a form is allowed to change.
Pattern Notes
- One Server Actions file per route. Co-locate the actions next to the
page that uses them; the import graph stays obvious and the
'use server'boundary is easy to audit. revalidatePathfor any data the user just changed. Skipping it is the most common reason a form submission “did nothing” — the action ran, the page just rendered stale HTML afterwards.- Validate at the action, not the form. Anyone can craft a request that bypasses HTML validation; trust only what your zod schema accepts.
- Assign one field at a time. Spreading parsed input into the model lets
hidden form fields land in the database. Explicit
comment.body = input.bodylines spell out the writable surface.
What’s Next
- Querying in Server Components — the reads that pair with these writes.
- Inserting and Updating Models — full
reference for
createandsave. - Authentication with JWT — adapt the hashing pattern for a Next.js sign-up Server Action.