Local state, global stores, and Zustand patterns.
State location decisions:
- Keep UI state in leaf components (default)
- Lift state to parent when multiple children need it
- Use Zustand global store for cross-cutting concerns (theme, user, org)
- Use Zustand contextual store for component-scoped state with cleanup
Avoid:
- Prop drilling through 3+ component layers (use Zustand instead)
- Subscribing to state only used in callbacks (read on-demand)
Keep local UI state in leaf components:
Lift state when multiple components need it:
When to use global store:
- State needed across many components (theme, user, org)
- State should persist across navigation
- Application-wide configuration
When to use contextual store:
- Form state that should reset on unmount
- Temporary UI state (modals, dropdowns)
- Component-specific data that shouldn't persist globally
Use createStore from zustand for application-wide state.
With Prepared patterns:
Contextual Zustand Stores
Use createContextualStore from @prepared/zustand for component-scoped state with automatic cleanup.
When to use contextual stores:
- Form state that should reset on unmount
- Temporary UI state
- Component-specific data that shouldn't persist
When NOT to use:
- Application-wide state (use global store)
- State that should persist across navigation
Don't pass props through 3+ component layers:
When prop drilling is acceptable:
- Props passed through 1-2 levels
- Props specific to a feature/component tree
- Props don't change frequently
Don't subscribe to state only used in callbacks—read on-demand:
Lazy State Initialization
Pass a function to useState for expensive initial values:
Use lazy initialization for:
- localStorage/sessionStorage reads
- Building data structures (indexes, maps)
- DOM reads
- Heavy transformations
With Prepared patterns:
For more detailed technical guidance, see these reference files: