Handling Forms via Form ActionsEntry 2 of 10
Validating Inside an Action
Checking the submitted values in the action function and collecting error messages to show the user.
Notes
- Keep rules as small reusable functions in
util/validation.ts(isEmail,isNotEmpty,hasMinLength,isEqualToOtherValue). - Collect every failure into an
errorsarray, so the user sees all problems at once. - An action on its own can't show errors: a plain function's return value goes nowhere.
useActionState(Entry 3) turns the return value into state the component can render. - A checkbox that isn't checked is missing from
FormData, soformData.get('terms')isnull. CheckformData.has('terms')orformData.get('terms') === 'on'.
What / Why / When
- What
Server-style validation, run on submit.
- Why
One place to check every rule, including cross-field rules like password confirmation.
- When
Any form action that sends data.
- Use case
Signup with email, password + confirmation, names, role, terms, and acquisition channels.
Code compare
// util/validation.tsexport const isEmail = (value: string) => value.includes("@");export const isNotEmpty = (value: string) => value.trim() !== "";export const hasMinLength = (value: string, min: number) => value.length >= min;export const isEqualToOtherValue = (value: string, other: string) => value === other;Validation lives on the form model. A cross-field rule (password confirmation) is a group validator.
// validators.tsexport function equalValues(a: string, b: string): ValidatorFn { return (group) => (group.get(a)?.value === group.get(b)?.value ? null : { valuesNotEqual: true });}export function atLeastOneChecked(group: AbstractControl): ValidationErrors | null { return Object.values(group.value).some(Boolean) ? null : { noneChecked: true };}Differences and gotchas
- React validates once, on submit, inside the action. Angular validates continuously and you choose when to show errors (
touched,markAllAsTouched()). Validators.requiredTrueis Angular's "checkbox must be checked."- Validation in the browser is never enough on its own. Your backend (or a Next.js Server Action) must validate again.
Quick reference
| Rule | React (in the action) | Angular |
|---|---|---|
isEmail(email) | Validators.email | |
| Min length | hasMinLength(pw, 6) | Validators.minLength(6) |
| Passwords match | isEqualToOtherValue(pw, confirm) | Group validator |
| Terms checked | formData.has('terms') | Validators.requiredTrue |
| At least one checkbox | getAll('acquisition').length > 0 | Group validator |