Back to docsRecipe

Settings form patterns

Production-tested patterns for building settings forms that handle validation, optimistic updates, and dirty-state tracking without external dependencies.

Controlled inputs

Keep form state in a single useReducer. Derive isDirty by comparing current values against the initial snapshot. Reset on successful save.

Validation

Run synchronous validators on every keystroke. Defer async checks (username availability, slug uniqueness) to onBlur. Accumulate errors in a flat map keyed by field name.

Save lifecycle

Disable the submit button while a save is in-flight. Show a spinner inside the button. On failure, surface the server error next to the offending field — never as a generic toast.

Unsaved changes guard

Attach a beforeunload listener when isDirty is true. Remove it on save or explicit discard. Pair with a client-side router guard for in-app navigation.

See the full implementation in Recipes index. Every pattern ships with a copy-pasteable example.