Forms are where users hand your application its most important input — their data, their decisions, their money — and a form done well is the difference between a flow users complete and one they abandon in frustration. In the App Router, forms have a first-class story built on Server Actions: you bind an action to a form, and submission runs that action on the server with the form's data, no manual fetch required. Around that core sit the pieces that make a form genuinely good: validation that runs on the server where it can be trusted and ideally on the client too for fast feedback, pending states that tell the user their submission is being processed, and a way to return validation errors and field values so a rejected form does not lose the user's work.
The reason forms deserve focused study, beyond just wiring an action, is that the experience details are exactly what users notice and what naive implementations get wrong. A form that gives no feedback while submitting leaves the user wondering whether their click registered, tempting a double-submit; a form that validates only on the client is trivially bypassed and insecure; a form that clears all fields when it rejects one of them forces the user to retype everything, which is infuriating. The App Router provides specific tools for each of these — the useFormStatus hook for pending state, the action's return value plus the useActionState hook for surfacing errors and preserving input, and the principle that validation must run server-side regardless of client checks. Understanding how these compose into a form that is responsive, secure, and forgiving is what separates a form users complete from one they rage-quit. This lesson covers the action-bound form, server and client validation, pending states, and error-and-value handling, and how they fit together.