{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/react-state-management",
  "version": "1.0.0",
  "name": "React State Management",
  "description": "Master modern React state management with Redux Toolkit, Zustand, Jotai, and React Query. Use when setting up global state, managing server state, or choosing between state management solutions.",
  "system_prompt_fragment": "# React State Management\n\nComprehensive guide to modern React state management patterns, from local component state to global stores and server state synchronization.\n\n## Do not use this skill when\n\n- The task is unrelated to react state management\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources/implementation-playbook.md`.\n\n## Use this skill when\n\n- Setting up global state management in a React app\n- Choosing between Redux Toolkit, Zustand, or Jotai\n- Managing server state with React Query or SWR\n- Implementing optimistic updates\n- Debugging state-related issues\n- Migrating from legacy Redux to modern patterns\n\n## Core Concepts\n\n### 1. State Categories\n\n| Type | Description | Solutions |\n|------|-------------|-----------|\n| **Local State** | Component-specific, UI state | useState, useReducer |\n| **Global State** | Shared across components | Redux Toolkit, Zustand, Jotai |\n| **Server State** | Remote data, caching | React Query, SWR, RTK Query |\n| **URL State** | Route parameters, search | React Router, nuqs |\n| **Form State** | Input values, validation | React Hook Form, Formik |\n\n### 2. Selection Criteria\n\n```\nSmall app, simple state → Zustand or Jotai\nLarge app, complex state → Redux Toolkit\nHeavy server interaction → React Query + light client state\nAtomic/granular updates → Jotai\n```\n\n## Quick Start\n\n### Zustand (Simplest)\n\n```typescript\n// store/useStore.ts\nimport { create } from 'zustand'\nimport { devtools, persist } from 'zustand/middleware'\n\ninterface AppState {\n  user: User | null\n  theme: 'light' | 'dark'\n  setUser: (user: User | null) => void\n  toggleTheme: () => void\n}\n\nexport const useStore = create<AppState>()(\n  devtools(\n    persist(\n      (set) => ({\n        user: null,\n        theme: 'light',\n        setUser: (user) => set({ user }),\n        toggleTheme: () => set((state) => ({\n          theme: state.theme === 'light' ? 'dark' : 'light'\n        })),\n      }),\n      { name: 'app-storage' }\n    )\n  )\n)\n\n// Usage in component\nfunction Header() {\n  const { user, theme, toggleTheme } = useStore()\n  return (\n    <header className={theme}>\n      {user?.name}\n      <button onClick={toggleTheme}>Toggle Theme</button>\n    </header>\n  )\n}\n```\n\n## Patterns\n\n### Pattern 1: Redux Toolkit with TypeScript\n\n```typescript\n// store/index.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'\nimport userReducer from './slices/userSlice'\nimport cartReducer from './slices/cartSlice'\n\nexport const store = configureStore({\n  reducer: {\n    user: userReducer,\n    cart: cartReducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware({\n      serializableCheck: {\n        ignoredActions: ['persist/PERSIST'],\n      },\n    }),\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\n// Typed hooks\nexport const useAppDispatch: () => AppDispatch = useDispatch\nexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector\n```\n\n```typescript\n// store/slices/userSlice.ts\nimport { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'\n\ninterface User {\n  id: string\n  email: string\n  name: string\n}\n\ninterface UserState {\n  current: User | null\n  status: 'idle' | 'loading' | 'succeeded' | 'failed'\n  error: string | null\n}\n\nconst initialState: UserState = {\n  current: null,\n  status: 'idle',\n  error: null,\n}\n\nexport const fetchUser = createAsyncThunk(\n  'user/fetchUser',\n  async (userId: string, { rejectWithValue }) => {\n    try {\n      const response = await fetch(`/api/users/${userId}`)\n      if (!response.ok) throw new Error('Failed to fetch user')\n      return await response.json()\n    } catch (error) {\n      return rejectWithValue((error as Error).message)\n    }\n  }\n)\n\nconst userSlice = createSlice({\n  name: 'user',\n  initialState,\n  reducers: {\n    setUser: (state, action: PayloadAction<User>) => {\n      state.current = action.payload\n      state.status = 'succeeded'\n    },\n    clearUser: (state) => {\n      state.current = null\n      state.status = 'idle'\n    },\n  },\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchUser.pending, (state) => {\n        state.status = 'loading'\n        state.error = null\n      })\n      .addCase(fetchUser.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.current = action.payload\n      })\n      .addCase(fetchUser.rejected, (state, action) => {\n        state.status = 'failed'\n        state.error = action.payload as string\n      })\n  },\n})\n\nexport const { setUser, clearUser } = userSlice.actions\nexport default userSlice.reducer\n```\n\n### Pattern 2: Zustand with Slices (Scalable)\n\n```typescript\n// store/slices/createUserSlice.ts\nimport { StateCreator } from 'zustand'\n\nexport interface UserSlice {\n  user: User | null\n  isAuthenticated: boolean\n  login: (credentials: Credentials) => Promise<void>\n  logout: () => void\n}\n\nexport const createUserSlice: StateCreator<\n  UserSlice & CartSlice, // Combined store type\n  [],\n  [],\n  UserSlice\n> = (set, get) => ({\n  user: null,\n  isAuthenticated: false,\n  login: async (credentials) => {\n    const user = await authApi.login(credentials)\n    set({ user, isAuthenticated: true })\n  },\n  logout: () => {\n    set({ user: null, isAuthenticated: false })\n    // Can access other slices\n    // get().clearCart()\n  },\n})\n\n// store/index.ts\nimport { create } from 'zustand'\nimport { createUserSlice, UserSlice } from './slices/createUserSlice'\nimport { createCartSlice, CartSlice } from './slices/createCartSlice'\n\ntype StoreState = UserSlice & CartSlice\n\nexport const useStore = create<StoreState>()((...args) => ({\n  ...createUserSlice(...args),\n  ...createCartSlice(...args),\n}))\n\n// Selective subscriptions (prevents unnecessary re-renders)\nexport const useUser = () => useStore((state) => state.user)\nexport const useCart = () => useStore((state) => state.cart)\n```\n\n### Pattern 3: Jotai for Atomic State\n\n```typescript\n// atoms/userAtoms.ts\nimport { atom } from 'jotai'\nimport { atomWithStorage } from 'jotai/utils'\n\n// Basic atom\nexport const userAtom = atom<User | null>(null)\n\n// Derived atom (computed)\nexport const isAuthenticatedAtom = atom((get) => get(userAtom) !== null)\n\n// Atom with localStorage persistence\nexport const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light')\n\n// Async atom\nexport const userProfileAtom = atom(async (get) => {\n  const user = get(userAtom)\n  if (!user) return null\n  const response = await fetch(`/api/users/${user.id}/profile`)\n  return response.json()\n})\n\n// Write-only atom (action)\nexport const logoutAtom = atom(null, (get, set) => {\n  set(userAtom, null)\n  set(cartAtom, [])\n  localStorage.removeItem('token')\n})\n\n// Usage\nfunction Profile() {\n  const [user] = useAtom(userAtom)\n  const [, logout] = useAtom(logoutAtom)\n  const [profile] = useAtom(userProfileAtom) // Suspense-enabled\n\n  return (\n    <Suspense fallback={<Skeleton />}>\n      <ProfileContent profile={profile} onLogout={logout} />\n    </Suspense>\n  )\n}\n```\n\n### Pattern 4: React Query for Server State\n\n```typescript\n// hooks/useUsers.ts\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\n\n// Query keys factory\nexport const userKeys = {\n  all: ['users'] as const,\n  lists: () => [...userKeys.all, 'list'] as const,\n  list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,\n  details: () => [...userKeys.all, 'detail'] as const,\n  detail: (id: string) => [...userKeys.details(), id] as const,\n}\n\n// Fetch hook\nexport function useUsers(filters: UserFilters) {\n  return useQuery({\n    queryKey: userKeys.list(filters),\n    queryFn: () => fetchUsers(filters),\n    staleTime: 5 * 60 * 1000, // 5 minutes\n    gcTime: 30 * 60 * 1000, // 30 minutes (formerly cacheTime)\n  })\n}\n\n// Single user hook\nexport function useUser(id: string) {\n  return useQuery({\n    queryKey: userKeys.detail(id),\n    queryFn: () => fetchUser(id),\n    enabled: !!id, // Don't fetch if no id\n  })\n}\n\n// Mutation with optimistic update\nexport function useUpdateUser() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: updateUser,\n    onMutate: async (newUser) => {\n      // Cancel outgoing refetches\n      await queryClient.cancelQueries({ queryKey: userKeys.detail(newUser.id) })\n\n      // Snapshot previous value\n      const previousUser = queryClient.getQueryData(userKeys.detail(newUser.id))\n\n      // Optimistically update\n      queryClient.setQueryData(userKeys.detail(newUser.id), newUser)\n\n      return { previousUser }\n    },\n    onError: (err, newUser, context) => {\n      // Rollback on error\n      queryClient.setQueryData(\n        userKeys.detail(newUser.id),\n        context?.previousUser\n      )\n    },\n    onSettled: (data, error, variables) => {\n      // Refetch after mutation\n      queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) })\n    },\n  })\n}\n```\n\n### Pattern 5: Combining Client + Server State\n\n```typescript\n// Zustand for client state\nconst useUIStore = create<UIState>((set) => ({\n  sidebarOpen: true,\n  modal: null,\n  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),\n  openModal: (modal) => set({ modal }),\n  closeModal: () => set({ modal: null }),\n}))\n\n// React Query for server state\nfunction Dashboard() {\n  const { sidebarOpen, toggleSidebar } = useUIStore()\n  const { data: users, isLoading } = useUsers({ active: true })\n  const { data: stats } = useStats()\n\n  if (isLoading) return <DashboardSkeleton />\n\n  return (\n    <div className={sidebarOpen ? 'with-sidebar' : ''}>\n      <Sidebar open={sidebarOpen} onToggle={toggleSidebar} />\n      <main>\n        <StatsCards stats={stats} />\n        <UserTable users={users} />\n      </main>\n    </div>\n  )\n}\n```\n\n## Best Practices\n\n### Do's\n- **Colocate state** - Keep state as close to where it's used as possible\n- **Use selectors** - Prevent unnecessary re-renders with selective subscriptions\n- **Normalize data** - Flatten nested structures for easier updates\n- **Type everything** - Full TypeScript coverage prevents runtime errors\n- **Separate concerns** - Server state (React Query) vs client state (Zustand)\n\n### Don'ts\n- **Don't over-globalize** - Not everything needs to be in global state\n- **Don't duplicate server state** - Let React Query manage it\n- **Don't mutate directly** - Always use immutable updates\n- **Don't store derived data** - Compute it instead\n- **Don't mix paradigms** - Pick one primary solution per category\n\n## Migration Guides\n\n### From Legacy Redux to RTK\n\n```typescript\n// Before (legacy Redux)\nconst ADD_TODO = 'ADD_TODO'\nconst addTodo = (text) => ({ type: ADD_TODO, payload: text })\nfunction todosReducer(state = [], action) {\n  switch (action.type) {\n    case ADD_TODO:\n      return [...state, { text: action.payload, completed: false }]\n    default:\n      return state\n  }\n}\n\n// After (Redux Toolkit)\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [],\n  reducers: {\n    addTodo: (state, action: PayloadAction<string>) => {\n      // Immer allows \"mutations\"\n      state.push({ text: action.payload, completed: false })\n    },\n  },\n})\n```\n\n## Resources\n\n- [Redux Toolkit Documentation](https://redux-toolkit.js.org/)\n- [Zustand GitHub](https://github.com/pmndrs/zustand)\n- [Jotai Documentation](https://jotai.org/)\n- [TanStack Query](https://tanstack.com/query)",
  "applicable_domains": [
    "frontend"
  ],
  "category": "frontend",
  "invocation": [
    "/react-state-management"
  ],
  "authored_by": "claudeskills.in community",
  "source_url": "https://claudeskills.in/skill/react-state-management",
  "provenance": {
    "source": "claudeskills.in",
    "source_url": "https://claudeskills.in/skill/react-state-management",
    "license": "unknown",
    "imported_at": "2026-09-03",
    "notes": "Aggregated by claudeskills.in from community GitHub lists."
  },
  "tags": [
    "claudeskills",
    "frontend"
  ],
  "lifecycle": "draft"
}