UXDL Docs

State Management

Zustand stores, server state with React Query, and caching patterns.

When to use what

LayerToolExamples
Server stateTanStack QueryAPI data, lists, detail views
Client UI stateZustandSidebar, filters, wizard steps
Form stateReact Hook FormMulti-step forms, validation
URL stateRouter search paramsPagination, active tab

Zustand store pattern

typescript
import { create } from "zustand";
 
type UiState = {
  sidebarOpen: boolean;
  activeWorkspace?: string;
  toggleSidebar: () => void;
  setWorkspace: (id: string) => void;
  reset: () => void;
};
 
export const useUiStore = create<UiState>((set) => ({
  sidebarOpen: true,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
  setWorkspace: (id) => set({ activeWorkspace: id }),
  reset: () => set({ sidebarOpen: true, activeWorkspace: undefined }),
}));

Server state with React Query

typescript
import { useQuery } from "@tanstack/react-query";
 
export function useProjects() {
  return useQuery({
    queryKey: ["projects"],
    queryFn: () => apiFetch("/v1/projects"),
    staleTime: 5 * 60 * 1000,
  });
}

Conventions

  • One store file per domain (stores/session.ts, stores/filters.ts)
  • Never put server-fetched data in Zustand — use React Query
  • Reset stores on logout
  • Avoid prop drilling — use stores or context at layout level