Challenges🏛ïļ ArchitectureState Management at Scale
ðŸĶ–DinosaurArchitectureState ManagementReact

State Management at Scale

Design a state management strategy for a large application with 50+ features, real-time data, and offline support.

State Management at Scale

The Prompt

Design the state management architecture for an enterprise application with 50+ feature modules, real-time updates, offline capability, and 20+ developers contributing simultaneously.

State Categories

Not all state is equal. Categorize it:

CategoryExamplesTool
Server stateAPI data, user profilesReact Query / SWR
Client stateUI toggles, form dataZustand / Redux
URL stateFilters, pagination, tabsURL search params
Form stateInput values, validationReact Hook Form
Derived stateComputed values, filtered listsSelectors / useMemo

Architecture

URL State (source of truth for shareable state)
  ↓
Server State Cache (React Query — async, deduplicated)
  ↓
Client Store (Zustand — UI state, preferences)
  ↓
Component State (useState — ephemeral, local)
  ↓
Derived State (selectors — computed, memoized)

Server State with React Query

function useProducts(filters) {
  return useQuery({
    queryKey: ['products', filters],
    queryFn: () => api.getProducts(filters),
    staleTime: 30_000,
    gcTime: 5 * 60_000,
    placeholderData: keepPreviousData,
  });
}
  • Automatic caching and deduplication
  • Background refetching
  • Optimistic updates for mutations
  • DevTools for debugging

Client State with Zustand

const useUIStore = create((set) => ({
  sidebarOpen: true,
  activeModal: null,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
  openModal: (id) => set({ activeModal: id }),
  closeModal: () => set({ activeModal: null }),
}));

URL State for Shareability

function useFilters() {
  const [searchParams, setSearchParams] = useSearchParams();
 
  const filters = useMemo(() => ({
    category: searchParams.get('category') ?? 'all',
    sort: searchParams.get('sort') ?? 'newest',
    page: Number(searchParams.get('page') ?? 1),
  }), [searchParams]);
 
  const setFilter = useCallback((key, value) => {
    setSearchParams((prev) => {
      prev.set(key, value);
      if (key !== 'page') prev.set('page', '1');
      return prev;
    });
  }, [setSearchParams]);
 
  return { filters, setFilter };
}

Offline Support

  1. Persistence: Serialize critical state to IndexedDB
  2. Optimistic mutations: Apply changes locally, queue for server
  3. Conflict resolution: Last-write-wins or server-side merge
  4. Background sync: Service worker retries failed mutations

Guidelines for 20+ Developers

  1. Naming conventions — use[Feature][Entity] for hooks
  2. Co-location — state lives near the feature that uses it
  3. No global stores — except for truly global state (auth, theme)
  4. Documentation — each state slice has a README explaining its purpose

The senior insight: the best state management architecture is the one where most developers never think about state management. Make the right patterns the easy patterns.