| name | state-management |
| description | Comprehensive state management patterns covering local vs global state, Redux Toolkit, Zustand, Jotai, signals, server state with TanStack Query, URL state, form state, optimistic updates, and state machines with XState.
Use when the user asks about state management, state management best practices, or needs guidance on state management implementation.
Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.
|
| license | Apache-2.0 |
| metadata | {"author":"foundry-skills","version":"1.0.0","tags":"web-development frontend design-patterns","category":"web-development","subcategory":"frontend-frameworks","depends":"","disclaimer":"none","difficulty":"intermediate"} |
State Management
Purpose
Select and implement the right state management approach for each use case in a web application. This skill provides decision frameworks, implementation patterns, and best practices for every category of application state.
State Category Classification
CATEGORY EXAMPLES RECOMMENDED SOLUTION
---------------------------------------------------------------------------
UI State Modal open/close, accordion, useState / useReducer
(ephemeral) tooltip visible, active tab (component-local)
Shared UI State Sidebar collapsed, theme, Context / Zustand / Jotai
notification queue
Server State API data, user profile, TanStack Query / SWR
(cached) list of items
URL State Current page, filters, URL search params (nuqs)
sort order, pagination
Form State Input values, validation, React Hook Form / Formik
dirty/touched tracking
Auth State Current user, tokens, Zustand / Context
permissions
Complex Workflows Multi-step forms, checkout, XState (state machines)
onboarding flows
Decision Tree
Is the state only used by one component?
YES -> useState or useReducer
NO -> Is it server/API data?
YES -> TanStack Query (cache, sync, refetch)
NO -> Is it URL-representable (shareable/bookmarkable)?
YES -> URL search params
NO -> Is it form data?
YES -> React Hook Form
NO -> Is it used by many components?
YES -> How many updates per second?
HIGH (>10) -> Zustand or Jotai (avoid Context)
LOW -> Context or Zustand
NO -> Lift state up to nearest common ancestor
Redux Toolkit Patterns
When to Use Redux
USE Redux Toolkit when:
- Large team needs predictable, traceable state changes
- Complex state with many interdependent slices
- Time-travel debugging is valuable
- Middleware is needed (logging, persistence, sync)
- Already using Redux in existing codebase
DO NOT use Redux when:
- Small app with simple state
- Mostly server state (use TanStack Query instead)
- Team is small and doesn't need enforced patterns
Slice Pattern
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
export const fetchTodos = createAsyncThunk(
'todos/fetchAll',
async (filters: TodoFilters, { rejectWithValue }) => {
try {
const response = await api.getTodos(filters);
return response.data;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
const todosSlice = createSlice({
name: 'todos',
# ... (condensed) ...
export const selectAllTodos = (state: RootState) => state.todos.items;
export const selectActiveTodos = createSelector(
selectAllTodos,
(todos) => todos.filter(t => !t.completed)
);
Zustand
When to Use Zustand
USE Zustand when:
- Need global state without boilerplate
- Want React-external state access (outside components)
- Need fine-grained subscriptions (no unnecessary re-renders)
- Simpler alternative to Redux for medium-complexity state
- Need middleware (persist, devtools, immer)
Store Pattern
import { create } from 'zustand';
import { devtools, persist, immer } from 'zustand/middleware';
interface CartState {
items: CartItem[];
total: number;
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
updateQuantity: (id: string, quantity: number) => void;
clear: () => void;
}
export const useCartStore = create<CartState>()(
devtools(
persist(
immer((set, get) => ({
items: [],
total: 0,
# ... (condensed) ...
function CartCount() {
const count = useCartStore((state) => state.items.length);
;
}
{ addItem, items } = useCartStore.();
Jotai (Atomic State)
When to Use Jotai
USE Jotai when:
- Need fine-grained reactivity (individual atoms)
- Want bottom-up state composition
- Complex derived state relationships
- Avoiding unnecessary re-renders is critical
- State shape is dynamic or unknown at build time
Atom Patterns
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
import { atomWithStorage, atomFamily } from 'jotai/utils';
const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);
const countWithMinAtom = atom(
(get) => get(countAtom),
(get, set, newValue: number) => {
set(countAtom, Math.max(0, newValue));
}
);
# ... (condensed) ...
);
function TodoItem({ id }: { id: string }) {
const [todo] = useAtom(todoAtomFamily(id));
return <div>{todo.title}</div>;
}
Server State (TanStack Query)
Core Patterns
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function useUsers(filters: UserFilters) {
return useQuery({
queryKey: ['users', filters],
queryFn: () => api.getUsers(filters),
staleTime: 5 * 60 * 1000,
gcTime: 30 * 60 * 1000,
retry: 3,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
placeholderData: (previousData) => previousData,
select: (data) => data.filter(u => u.isActive),
});
}
function () {
# ... (condensed) ...
: [, ],
: api.({ : pageParam, : }),
:
lastPage. === ? allPages. * : ,
: ,
});
}
URL State
import { useQueryState, parseAsInteger, parseAsStringEnum } from 'nuqs';
function ProductList() {
const [search, setSearch] = useQueryState('q', { defaultValue: '' });
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
const [sort, setSort] = useQueryState('sort',
parseAsStringEnum(['price', 'name', 'rating']).withDefault('name')
);
const [category, setCategory] = useQueryState('cat');
}
function useURLState<T>(key: string, defaultValue: T, parse: (v: string) => T) {
const [searchParams, setSearchParams] = useSearchParams();
# ... (condensed) ...
}
return next;
});
}, [key, defaultValue, setSearchParams]);
return [value, setValue] as ;
}
Form State
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Minimum 8 characters'),
role: z.enum(['admin', 'user', 'editor']),
preferences: z.object({
newsletter: z.boolean(),
notifications: z.boolean(),
}),
});
type FormData = z.infer<typeof schema>;
function RegistrationForm() {
# ... (condensed) ...
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span role="alert">{errors.email.message}</>}
{}
</form>
);
}
State Machines (XState)
When to Use State Machines
USE XState when:
- Complex UI flows with many states and transitions
- Impossible states must be prevented (not just handled)
- Business logic that must be visualized and validated
- Multi-step processes (checkout, onboarding, wizards)
- Authentication flows
- Real-time features with connection states
Examples of good fits:
- Checkout: idle -> cart -> shipping -> payment -> confirmation
- Auth: loggedOut -> loggingIn -> loggedIn -> loggingOut
- Upload: idle -> selecting -> uploading -> processing -> complete | error
- Connection: disconnected -> connecting -> connected -> reconnecting
Machine Pattern
import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/react';
const uploadMachine = createMachine({
id: 'upload',
initial: 'idle',
context: {
file: null as File | null,
progress: 0,
error: null as string | null,
url: null as string | null,
},
states: {
idle: {
on: {
SELECT_FILE: {
target: 'selected',
actions: assign({ file: (_, event) => event.file }),
# ... (condensed) ...
{state.matches('selected') && <button onClick={() => send('UPLOAD')}>Upload</button>}
{state.matches('uploading') && < = />}
{state.() && }
{state.() && }
</div>
);
}
State Management Checklist
When to Use
Use this skill when:
- Designing or implementing state management solutions
- Reviewing or improving existing state management approaches
- Making architectural or implementation decisions about state management
- Learning state management patterns and best practices
- Troubleshooting state management-related issues
Do NOT use this skill when:
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance
Output Format
# State Management Analysis
## Context Assessment
[Situation summary and constraints]
## Recommended Approach
[Primary recommendation with rationale]
## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]
## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]
## Next Steps
- [Immediate action item]
- [Follow-up action item]
Example
Input: "Help me implement state management for a medium-scale production application"
Output: A structured analysis covering current state assessment, recommended state management approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.
Edge Cases
- Legacy system integration: When state management must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- Scale mismatch: When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- Team skill gaps: When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- Conflicting requirements: When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities