> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# State management

> Where to put state — Convex, URL, localStorage, React state, Zustand. The decision tree.

Where state lives shapes everything else. The framework: **put state at the lowest layer that satisfies the requirement.**

## Decision tree

<Steps>
  <Step title="Should everyone see this state? (Multi-user, persistent.)">
    **Yes:** Convex (mutation writes to DB; queries read).
  </Step>

  <Step title="Should it survive a page refresh? (Single-user, persistent.)">
    **Yes:** localStorage (or per-user Convex row if multi-device).
  </Step>

  <Step title="Should it be shareable via URL? (Filters, tabs, search.)">
    **Yes:** URL query parameters.
  </Step>

  <Step title="Multi-component, ephemeral?">
    **Yes:** Zustand (or React context for tree-scoped).
  </Step>

  <Step title="Single component, ephemeral?">
    **Yes:** `useState` / `useReducer`.
  </Step>
</Steps>

## Examples

| State | Where |
| - | - |
| List of tasks | Convex |
| Current user | Convex (queried via auth) |
| Active workspace | URL (`/w/{slug}/...`) |
| Filter selection on the task list | URL (so it survives refresh and is shareable) |
| Modal open/closed | `useState` |
| Sidebar collapsed | localStorage (persists across sessions) |
| Multi-step form draft | Zustand (or localStorage if you want survival across refresh) |
| Theme preference | localStorage + cookie (server-side render) |

## Common mistakes

<AccordionGroup>
  <Accordion title="Putting Convex data in Zustand" icon="x-circle">
    `useQuery` *is* the cache. Don't copy results into a Zustand store; you'll fight with stale data.
  </Accordion>

  <Accordion title="Putting filter state in component state" icon="x-circle">
    A user sets filters, refreshes, all gone. Use URL query params instead.
  </Accordion>

  <Accordion title="Storing transient form state in Convex" icon="x-circle">
    Each keystroke = a mutation = wasted credits and DB churn. Use local state; persist on submit.
  </Accordion>
</AccordionGroup>

## Tips

<Tip>
  **For collaborative editing, every keystroke goes to Convex.** That's not waste — that's the feature. Reactive sync makes co-editing automatic.
</Tip>

<Tip>
  **Use `useReducer` for complex local state.** When `useState` calls multiply (5+ in a single component), a reducer simplifies.
</Tip>

## Related

<CardGroup cols={3}>
  <Card title="Convex queries" icon="search" href="/convex/queries">
    Server state.
  </Card>

  <Card title="Optimistic updates" icon="zap" href="/architecture/optimistic-updates">
    Pattern for instant client feedback.
  </Card>

  <Card title="Component patterns" icon="component" href="/architecture/component-patterns">
    Where state lives in your component tree.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.