Decision tree
1
Should everyone see this state? (Multi-user, persistent.)
Yes: Convex (mutation writes to DB; queries read).
2
Should it survive a page refresh? (Single-user, persistent.)
Yes: localStorage (or per-user Convex row if multi-device).
3
Should it be shareable via URL? (Filters, tabs, search.)
Yes: URL query parameters.
4
Multi-component, ephemeral?
Yes: Zustand (or React context for tree-scoped).
5
Single component, ephemeral?
Yes:
useState / useReducer.Examples
Common mistakes
Putting Convex data in Zustand
Putting Convex data in Zustand
useQuery is the cache. Don’t copy results into a Zustand store; you’ll fight with stale data.Putting filter state in component state
Putting filter state in component state
A user sets filters, refreshes, all gone. Use URL query params instead.
Storing transient form state in Convex
Storing transient form state in Convex
Each keystroke = a mutation = wasted credits and DB churn. Use local state; persist on submit.
Tips
Related
Convex queries
Server state.
Optimistic updates
Pattern for instant client feedback.
Component patterns
Where state lives in your component tree.
