state-management
Skill9 starsChanged 20 days agoMITView on GitHub
State management patterns for React apps — Zustand, TanStack Query, Context, URL state. Guides architecture decisions for local, global, server, and URL state. Keywords: state management, zustand, tanstack query, react query, global state, server state, data fetching, caching
- Installs packages
27 other projects use a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- komluk/scaffoldingtheir own version, first seen Nov 7, 2026
- compozy/compozytheir own version, first seen Nov 7, 2026
- omspradippatil/Claude-Skillstheir own version, first seen Nov 1, 2026
- github/replicatheir own version, first seen Nov 1, 2026
- darknecrocities/SkillBuildertheir own version, first seen Nov 1, 2026
- TheBeardedBearSAS/claude-crafttheir own version, first seen Oct 29, 2026
Used with
Projects with it also use css-architecture, error-handling and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Piyush8296Skillcss-architecture
CSS architecture, Tailwind patterns, responsive design, design tokens, and styling best practices. Use when implementing designs, setting up styling infrastructure, or debugging layout issues.
Also in 3 projects that use this
MicrosoftSkillerror-handling
统一错误处理系统。在添加 API 端点、修改错误处理、添加前端 API 调用、编写错误相关测试时使用。
Also in 6 projects that use this
lbednerSkilli18n
Use when a code or template change introduces a new translation message key, in either the framework CLI (aegis/i18n/locales/) or a generated project's CLI (the app/i18n/locales/ template tree). Covers adding the real English string, stubbing the same key into every other locale for parity, and the separate translation PR that later replaces placeholders with reviewed native text.
Also in 4 projects that use this
mijunriSkilldatabase
数据库脚本与操作规范。脚本放在 db_scripts 目录,遵循查询优化、事务、安全等实践。
Also in 3 projects that use this
genomewalkerSkillresume
Resume a thread by loading its ~800-token context capsule
Also in 3 projects that use this
NVIDIASkilltesting
Run Python and Rust tests for physicsnemo-curator using uv + pytest with coverage reporting via pytest-cov, and cargo-nextest for Rust tests. Includes benchmark workflows with pytest-benchmark and criterion.
Also in 6 projects that use this
Other versions27 projects use a version of this.
Word for word · 0
None found.
Edited · 1
- PMDevSolutions/Claudius★ 199% same
Wrote their own version · 26
- compozy/compozy★ 2.9ksame name
- github/replica★ 230same name
- conorluddy/xclaude-plugin★ 186same name
- cohen-liel/hivemind★ 108same name
- TheBeardedBearSAS/claude-craft★ 108same name
- EmeaAppGbb/spec2cloud★ 100same name
- statelyai/skills★ 48same name
- komluk/scaffolding★ 16same name
- darknecrocities/SkillBuilder★ 13same name
- YepAPI/skills★ 8same name
16 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository32 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- animation-motionSkill
- auth-flowsSkill
- build-from-canvaSkill
- build-from-conversationSkill
- build-from-figmaSkill
- build-from-screenshotSkill
- canva-intakeSkill
- canva-token-inferenceSkill
- ciSkill
- conversation-intakeSkill
- design-brief-to-figmaSkill
- design-token-lockSkill
- e2e-test-generatorSkill
- export-design-systemSkill
- figma-intakeSkill
- figma-to-react-workflowSkill
- form-handlingSkill
- indesign-conversionSkill
- lintSkill
- parallel-orchestrationSkill
- react-accessibilitySkill
- react-component-developmentSkill
- react-performance-optimizationSkill
- react-testing-workflowsSkill
- screenshot-intakeSkill
- seo-metadataSkill
- storybook-story-generationSkill
- tdd-from-figmaSkill
- testSkill
- verify-allSkill
- visual-qa-verificationSkill
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 6 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 21 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 65
Rule: If you have 3+ related useState calls that change together, switch to useReducer.
- Distinctive: line 118
Always use selectors for reads
- Very specific: line 119
Use persist middleware for state that survives refresh (theme, user preferences)
- Very specific: line 120
Use devtools middleware in development
- Distinctive: line 121
Keep actions in the store, not in components
- Distinctive: line 122
Never store server data in Zustand — use TanStack Query
- Distinctive: line 126
Use for: All data from APIs. Handles caching, refetching, loading states, error states, optimistic updates.
- Distinctive: line 216
NEVER store API data in useState or Zustand
- Distinctive: line 217
Every API call gets its own custom hook
- Very specific: line 219
Use enabled to conditionally fetch
- Very specific: line 220
Use staleTime (not refetchInterval) for most cases
“Common” is measured: kept by at least 6 in 10 of the 21 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
State Management Patterns
When to Use This Skill
Use when:
- Setting up state management for a new React app
- Deciding between state solutions
- Implementing data fetching/caching
- Debugging stale state or unnecessary re-renders
- Migrating from Redux or other state libraries
State Categories Decision Tree
What kind of state?
├── UI State (local) → useState / useReducer
│ └── Shared across 2-3 components → Lift state up
├── UI State (global) → Zustand
│ └── Theme, sidebar open, modals, toasts
├── Server State → TanStack Query
│ └── API data, caching, background refetching
├── URL State → useSearchParams / nuqs
│ └── Filters, pagination, sort, tab selection
└── Form State → React Hook Form + Zod
└── Inputs, validation, submission1. Local State — useState / useReducer
Use for: Component-scoped UI state that doesn't need sharing.
// Simple toggle
const [isOpen, setIsOpen] = useState(false);
// Complex state with multiple transitions — use useReducer
type Action =
| { type: 'FETCH_START' }
| { type: 'FETCH_SUCCESS'; data: Item[] }
| { type: 'FETCH_ERROR'; error: string };
interface State {
items: Item[];
loading: boolean;
error: string | null;
}
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'FETCH_START':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { items: action.data, loading: false, error: null };
case 'FETCH_ERROR':
return { ...state, loading: false, error: action.error };
}
}Rule: If you have 3+ related useState calls that change together, switch to useReducer.
2. Global UI State — Zustand
Use for: State shared across many components (theme, auth status, notifications, layout).
Setup
pnpm add zustandStore Pattern
// src/stores/use-app-store.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface AppState {
theme: 'light' | 'dark';
sidebarOpen: boolean;
setTheme: (theme: 'light' | 'dark') => void;
toggleSidebar: () => void;
}
export const useAppStore = create<AppState>()(
devtools(
persist(
(set) => ({
theme: 'light',
sidebarOpen: true,
setTheme: (theme) => set({ theme }),
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}),
{ name: 'app-store' }
)
)
);Usage — Select Only What You Need
// GOOD — component re-renders only when theme changes
const theme = useAppStore((s) => s.theme);
// BAD — re-renders on ANY store change
const { theme } = useAppStore();Zustand Rules
- One store per domain (app, auth, notifications) — not one mega store
- Always use selectors for reads
- Use
persistmiddleware for state that survives refresh (theme, user preferences) - Use
devtoolsmiddleware in development - Keep actions in the store, not in components
- Never store server data in Zustand — use TanStack Query
3. Server State — TanStack Query
Use for: All data from APIs. Handles caching, refetching, loading states, error states, optimistic updates.
Setup
pnpm add @tanstack/react-query @tanstack/react-query-devtoolsProvider Setup
// src/providers/query-provider.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
gcTime: 5 * 60 * 1000,
retry: 1,
refetchOnWindowFocus: false,
},
},
});
export function QueryProvider({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}Query Pattern — Custom Hooks
// src/hooks/use-users.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
const userKeys = {
all: ['users'] as const,
lists: () => [...userKeys.all, 'list'] as const,
list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,
details: () => [...userKeys.all, 'detail'] as const,
detail: (id: string) => [...userKeys.details(), id] as const,
};
export function useUsers(filters: UserFilters) {
return useQuery({
queryKey: userKeys.list(filters),
queryFn: () => api.users.list(filters),
});
}
export function useUser(id: string) {
return useQuery({
queryKey: userKeys.detail(id),
queryFn: () => api.users.get(id),
enabled: !!id,
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: UpdateUserInput) => api.users.update(data),
onMutate: async (newData) => {
await queryClient.cancelQueries({ queryKey: userKeys.detail(newData.id) });
const previous = queryClient.getQueryData(userKeys.detail(newData.id));
queryClient.setQueryData(userKeys.detail(newData.id), (old: User) => ({
...old,
...newData,
}));
return { previous };
},
onError: (_err, newData, context) => {
queryClient.setQueryData(userKeys.detail(newData.id), context?.previous);
},
onSettled: (_data, _err, variables) => {
queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) });
},
});
}TanStack Query Rules
- NEVER store API data in useState or Zustand
- Every API call gets its own custom hook
- Query keys are structured objects (not strings)
- Use
enabledto conditionally fetch - Use
staleTime(not refetchInterval) for most cases - Optimistic updates for mutations that need instant feedback
- Invalidate related queries after mutations
4. URL State — Search Params
Use for: Filters, pagination, sort order, active tabs — anything the user should be able to bookmark or share.
Next.js (App Router)
'use client';
import { useSearchParams, useRouter, usePathname } from 'next/navigation';
export function useUrlState<T extends string>(key: string, defaultValue: T) {
const searchParams = useSearchParams();
const router = useRouter();
const pathname = usePathname();
const value = (searchParams.get(key) as T) ?? defaultValue;
const setValue = (newValue: T) => {
const params = new URLSearchParams(searchParams.toString());
if (newValue === defaultValue) {
params.delete(key);
} else {
params.set(key, newValue);
}
router.replace(`${pathname}?${params.toString()}`);
};
return [value, setValue] as const;
}Vite (with nuqs)
pnpm add nuqsimport { useQueryState, parseAsInteger } from 'nuqs';
function ProductList() {
const [search, setSearch] = useQueryState('q', { defaultValue: '' });
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
const [sort, setSort] = useQueryState('sort', { defaultValue: 'newest' });
}Anti-Patterns to Avoid
| Anti-Pattern | Instead |
| Storing API data in useState | Use TanStack Query |
| Prop drilling through 4+ levels | Zustand store or composition |
| Context for frequently changing values | Zustand (Context causes full tree re-render) |
| One giant Redux/Zustand store | Split by domain |
| useEffect to sync state | Derive it or use useMemo |
| Fetching in useEffect | TanStack Query |
| Manual loading/error states | TanStack Query gives these for free |
| Duplicating server state in client | Single source: TanStack Query cache |
Testing State
Zustand — test the store directly
import { useAppStore } from '@/stores/use-app-store';
beforeEach(() => {
useAppStore.setState({ theme: 'light', sidebarOpen: true });
});
test('toggleSidebar flips the value', () => {
useAppStore.getState().toggleSidebar();
expect(useAppStore.getState().sidebarOpen).toBe(false);
});TanStack Query — use wrapper
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';
function createWrapper() {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
return ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}
test('useUsers returns user list', async () => {
const { result } = renderHook(() => useUsers({}), { wrapper: createWrapper() });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toHaveLength(3);
});Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

