tezvyn:

How do you use a Server Action for form submission and useFormState?

AI-drafted, machine-checkedSource: nextjs.orgintermediate

Tests server-client form state orchestration. Outline: write a Server Action that validates and returns errors; pass it to useFormState for state; wire to form action and use useFormStatus for pending UI.

WHAT THIS TESTS: This question probes your understanding of Next.js App Router form patterns, specifically the progressive enhancement model where forms function without JavaScript yet still provide rich client feedback. The interviewer wants to see that you know how to keep validation logic on the server while surfacing results to the client declaratively. They also care whether you understand the distinction between form state and submission status.

A GOOD ANSWER COVERS: Four things in order. First, the Server Action itself must be an async function decorated with the use server directive, and it should accept two arguments when used with useFormState: the previous state and the form data. Second, validation happens inside the action using something like zod or manual checks, and the function returns a plain object containing errors or success messages rather than throwing exceptions, because thrown errors are harder to map to fields. Third, on the client, you call useFormState passing the imported Server Action and an initial state object; it returns a tuple of current state and the bound action. You place the bound action directly on the form action attribute, not on an onSubmit handler. Fourth, pending states are handled by a separate child component that calls useFormStatus, which reads the pending state of the nearest parent form; this keeps the pending UI co-located but avoids re-rendering the entire form on every keystroke.

COMMON WRONG ANSWERS: Candidates often say they would call the Server Action inside an onSubmit event handler and use local useState for errors. This defeats the purpose of useFormState and breaks progressive enhancement. Another red flag is trying to use useFormStatus in the same component that renders the form; the hook must be used inside a component rendered inside the form. Some candidates also suggest returning Response objects or throwing errors from the Server Action, which useFormState does not capture gracefully.

LIKELY FOLLOW-UPS: The interviewer might ask how you would optimistically update the UI while the action is pending. They could also ask what happens if the Server Action throws an uncaught error, or how you would revalidate the page cache after a successful mutation using revalidatePath or revalidateTag. Another follow-up is handling file uploads in the same Server Action.

ONE CONCRETE EXAMPLE: Imagine a newsletter signup form. The Server Action receives form data, checks if the email is already subscribed using a database query, and returns either state errors like email already exists or a success message. The client component initializes useFormState with an empty errors object. The form tag uses action={formAction}. Below the email input, you conditionally render state.errors.email. A SubmitButton component inside the form calls useFormStatus and disables itself while status.pending is true. If JavaScript is disabled, the form still submits and the page refreshes with the returned state.

Read the original → nextjs.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.