State Management
How client state, server state, URL state, and form state are managed across the Trovella web application.
Trovella uses a layered state management approach. Each layer has a dedicated tool chosen for its specific strengths, and the boundaries between layers are deliberate.
The State Layers
| Layer | Tool | Status | Purpose |
|---|---|---|---|
| Server state | TanStack Query v5 via tRPC React Query | Active | Cache, fetch, and synchronize server data |
| Client state | React useState / useCallback / useMemo | Active | Component-local and feature-scoped UI state |
| Client state (global) | Zustand | Planned | Cross-component client state (not yet needed) |
| URL state | nuqs | Planned | Shareable/bookmarkable UI state in query params |
| Form state | Controlled inputs + Zod schemas | Active | Form field values and validation |
| Form state (complex) | React Hook Form | Planned | Multi-step forms with complex validation |
The "planned" tools are accepted in the tech stack (see ADR-005: Framework Decision) but have not been installed yet because the existing patterns are sufficient for current features. They will be added when explicit complexity triggers are hit.
Current Architecture
Browser
|
+-- TanStack Query cache (server state)
| Managed by tRPC React Query hooks
| 30s stale time, automatic refetch
|
+-- React useState (client state)
| Component-local: pagination, filters, selections
| Feature-scoped: AI playground settings, stream state
|
+-- Controlled inputs (form state)
Direct useState + onChange handlers
Zod schemas from @repo/validators for server-side validation
Decision Criteria for Choosing a Layer
Use tRPC/TanStack Query when the data comes from the server. This is the most common case -- admin dashboards, research plans, settings, and all CRUD operations go through tRPC queries and mutations.
Use React useState for UI-only state that does not need to survive a page navigation: pagination offsets, expanded/collapsed rows, selected items, filter values, loading indicators.
Use Zustand (when added) for client state that must be shared across unrelated component subtrees without prop drilling. No current feature requires this.
Use nuqs (when added) for state that should be reflected in the URL so users can bookmark or share a specific view -- filter combinations, search queries, active tabs.
Use React Hook Form (when added) for forms with many fields, cross-field validation, or multi-step wizards. Current forms are simple enough that controlled inputs work well.
Pages in This Topic
| Page | What It Covers |
|---|---|
| Server State | tRPC React Query hooks, query patterns, cache invalidation, the provider tree |
| Client State | React useState patterns, custom hooks, and the path to Zustand |
| Form Handling | Controlled inputs, Zod validator schemas, and the path to React Hook Form |
Related Topics
- Routing & Pages -- Component Boundaries -- where the server/client split happens and the
Providerswrapper - Data & Storage -- Query Patterns -- the server-side tRPC procedure chain that feeds TanStack Query
- ADR-005: Framework Decision -- rationale for the five-library state management stack