Question presented to candidate: "Walk me through a form that creates a record in the App Router. Where does the mutation go, and how does the list update afterwards?"
What a strong answer should cover:
- The mutation is a function marked
"use server", passed directly to<form action>. No route file, no fetch, no client state for the request. - The step people forget: the cache. After mutating you call the framework's revalidation — by path or by tag — so the affected Server Components re-render on the next request.
- Without that, the write succeeds and the UI still shows stale data, which is the classic "it worked but nothing changed" bug.
- Because the server re-renders, no client refetch is needed — the updated markup streams back and replaces the affected segment.
- Errors: catch inside the action and return them as state via
useActionState; throwing reaches an error boundary, which is disproportionate for validation. - Redirect after a successful mutation is a server-side call, not a router push.
- For instant feedback,
useOptimisticshows the change before the round trip completes. - Security is not optional: the action is a public endpoint, so authentication, authorisation and validation live inside it.
Clarifying questions expected:
- "Does the list that needs updating live in a Server Component?" — that decides revalidation versus client state.
- "Should this feel instant, or is a spinner acceptable?" — that decides whether
useOptimisticis worth it.
Code / implementation expected: Yes — this is a workflow question and a short end-to-end sketch is the natural answer.