Form Libraries
Forms are where users meet friction. Every field is a chance to lose them.
The right form library removes boilerplate without hiding control. The wrong one fights your validation logic at scale.
Library Selection
| Criteria | TanStack Form | React Hook Form | Native HTML |
|---|---|---|---|
| Bundle size | ~15 KB | ~9 KB | 0 KB |
| Validation | Built-in + Zod/Valibot | Resolver pattern (Zod) | Manual |
| Render control | Fine-grained subscription | Uncontrolled by default | Full control |
| Server Components | Compatible | Compatible | Native |
| TypeScript | First-class | First-class | Manual types |
| Learning curve | Moderate | Low | None |
| Multi-step forms | Built-in | Manual orchestration | Manual |
| Best for | Complex forms, wizards | Simple-to-medium forms | One-off forms |
Key Patterns
| Pattern | When | Why |
|---|---|---|
| Schema validation | Always | Single source of truth for client and server |
| Progressive disclosure | Multi-step flows | Reduce cognitive load per screen |
| Optimistic submission | Low-risk actions | Faster perceived performance |
| Server-side validation | Always | Client validation is a courtesy, not security |
| Field-level errors | User-facing forms | Immediate feedback beats form-level error dumps |
Decision Rule
Start with native HTML forms and server actions. Add a library when you hit one of these: conditional fields, multi-step wizards, complex validation chains, or array fields. Not before.
Context
- State Management — Forms are state; pick the right boundary
- App Router — Server actions change form architecture
- Components — Form fields are components first
- Anti-patterns — Over-engineering forms is the top one
Questions
When does a form become complex enough to justify a library?
- What validation logic belongs on the server vs. the client?
- How do multi-step forms interact with URL state and browser back?
- What is the real cost of a form library you outgrow?