react-patterns
Skill8 starsChanged 8 days agoMITView on GitHub
Writes and reviews idiomatic React - minimal state with derived values, effects only for external systems, server state through a data library or framework loaders, correct keys, composition over prop drilling, measured memoization, error boundaries, Suspense, forms and actions, and the Server/Client Component boundary. Use when building or reviewing React components or hooks, fixing re-render, stale state or useEffect bugs, structuring state, data fetching and forms, or testing components in React, Next.js, React Router/Remix or Vite apps.
38 other projects use a version of this.
Adopted by
Most recently taken up by
- lhbsaa/apex-unifiedtheir own version, first seen Nov 8, 2026
- komluk/scaffoldingtheir own version, first seen Nov 7, 2026
- Lukk17/agent-standardstheir own version, first seen Nov 7, 2026
- ku5ic/claude-kittheir own version, first seen Nov 4, 2026
- renepf/claude-skillstheir own version, first seen Oct 25, 2026
- vindreshsingh/engineering-skillstheir own version, first seen Oct 20, 2026
Used with
Projects with it also use typescript-strict, docker-expert and 7 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.
MicrosoftSkilltypescript-strict
Enable TypeScript strict mode (strict, noImplicitAny, strictNullChecks, etc.) in a project and fix the resulting errors without weakening types, tests, or runtime behavior. Use when asked to "enable strict mode", "turn on TypeScript strict", "fix strict errors", or migrate a codebase to strict type checking.
Also in 3 projects that use this
xfstudioSkilldocker-expert
Docker containerization expert with deep knowledge of multi-stage builds, image optimization, container security, Docker Compose orchestration, and production deployment patterns. Use PROACTIVELY for Dockerfile optimization, container issues, image size problems, security hardening, networking, and orchestration challenges.
Also in 3 projects that use this
LangChainSkilldocker-patterns
Best practices for Docker containerization and multi-stage builds
Also in 4 projects that use this
BlockSkilltesting-strategy
Guidelines for writing effective tests in this project
Also in 4 projects that use this
MicrosoftSkillerror-handling
统一错误处理系统。在添加 API 端点、修改错误处理、添加前端 API 调用、编写错误相关测试时使用。
Also in 5 projects that use this
HABUBUSSSkillbackend-patterns
Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.
Also in 5 projects that use this
Other versions38 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 38
- openstory-so/openstory★ 696same name
- involvex/youtube-music-cli★ 477same name
- xenitV1/Antigravity-Workflows★ 130same name
- TangleML/tangle-ui★ 104same name
- maxyeh0817/Forge★ 34same name
- VoDaiLocz/kilo-kit-mcp★ 28same name
- Fandry96/k3-agentic-skills★ 27same name
- komluk/scaffolding★ 16same name
- xfstudio/skills★ 9same name
- gongyijie85/dsh-ecc★ 8same name
28 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 repository60 other files it gives its agents.
- api-designSkill
- auth-implementationSkill
- background-jobsSkill
- building-agentsSkill
- caching-strategiesSkill
- ci-cd-pipelinesSkill
- cloud-cost-optimizationSkill
- codebase-onboardingSkill
- code-review-changesSkill
- concurrency-patternsSkill
- database-migrationsSkill
- database-schema-designSkill
- database-testingSkill
- data-pipelinesSkill
- data-privacy-retentionSkill
- dependency-upgradesSkill
- design-docsSkill
- design-systemsSkill
- dockerfilesSkill
- domain-driven-designSkill
- event-driven-architectureSkill
- file-storage-uploadsSkill
- frontend-securitySkill
- git-workflowSkill
- graphql-apisSkill
- grpc-servicesSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- internationalizationSkill
- kubernetes-deploymentsSkill
- large-scale-migrationsSkill
- legacy-refactoringSkill
- llm-evalsSkill
- llm-integrationSkill
- mcp-serversSkill
- micro-frontendsSkill
- ml-experimentsSkill
- monorepo-managementSkill
- nosql-designSkill
- observabilitySkill
- orm-patternsSkill
- performance-profilingSkill
- postgres-operationsSkill
- rag-systemsSkill
- realtime-apisSkill
- resilience-patternsSkill
- root-cause-debuggingSkill
- search-integrationSkill
- security-auditSkill
- seo-and-ssrSkill
- sql-optimizationSkill
- system-designSkill
- test-strategySkill
- threat-modelingSkill
- typescript-patternsSkill
- unit-testingSkill
- verify-before-doneSkill
- web-accessibilitySkill
- web-performanceSkill
- writing-skillsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
17 rules. 0 are common, 9 are distinctive, and 8 are very specific to this project.
0
Common
Most of the 34 edited versions we read keep these.
9
Distinctive
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 17
Not for: React Native platform APIs, or styling questions.
- Very specific: line 21
Check the versions: Read package.json for the react version and the framework (Next.js App Router or Pages Router, React Router v7 / Remix, …
- Distinctive: line 60
Every effect that starts something must stop it:
- Distinctive: line 81
Use a stable, unique ID from the data.
- Distinctive: line 82
Don't use the array index for lists that can reorder, insert or delete.
- Very specific: line 83
Never generate keys during render (Math.random(), crypto.randomUUID()).
- Very specific: line 92
Never switch one input between controlled and uncontrolled (value={undefined} then a string).
- Distinctive: line 104
When context is right:
- Very specific: line 127
Put an error boundary at each route and around independent widgets so one failure doesn't blank the page.
- Very specific: line 129
Use <Suspense fallback={...}> with data sources that support it (framework loaders, useSuspenseQuery, use(promise) in React 19).
- Distinctive: line 130
Place Suspense boundaries where a loading state makes sense to the user, not around every component.
- Very specific: line 134
Only applies in frameworks with React Server Components (for example the Next.js App Router).
- Very specific: line 139
Import the server-only package in modules with secrets or DB access so they fail the build if imported into client code.
- Very specific: line 191
Use findBy* for content that appears asynchronously.
- Distinctive: line 193
Test what the user sees and does, not state variables or hook internals.
- Distinctive: line 209
No effect exists that only transforms data or responds to a user event.
- Distinctive: line 210
Every effect that subscribes, connects or starts a timer has cleanup.
“Common” is measured: kept by at least 6 in 10 of the 34 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
React Patterns
UI is a function of state. Keep the minimum state, compute everything else during render, and use effects only to synchronize with systems outside React. Most React bugs come from duplicated state and effects that should not exist.
When to use
- Writing or reviewing components, custom hooks or a React diff.
- Bugs: stale values, infinite re-render loops, flicker, "state is one step behind", lost input focus, slow typing.
- Deciding where state, data fetching or form handling should live.
Not for: React Native platform APIs, or styling questions.
Workflow
- Check the versions. Read
package.jsonfor thereactversion and the framework (Next.js App Router or Pages Router, React Router v7 / Remix, Vite SPA, Expo). React 19 APIs (actions,use, ref as a prop,<Context value>) needreact19+. Server Components need a framework that supports them. - Check for React Compiler (
babel-plugin-react-compiler, or the framework's compiler option). If it is on, don't add manualuseMemo/useCallback/memounless profiling shows a need. Check the installed version's docs for what it covers. - Classify every piece of data using the table below before writing state.
- Audit every
useEffectagainst references/effects.md. Delete the ones that don't sync with an external system. - Lint with
eslint-plugin-react-hooks. Fixexhaustive-depswarnings by restructuring code; never suppress them. - Test behavior with Testing Library (see Testing).
Where state lives
| Kind | Example | Put it in |
| Server data | users, orders | A data library (TanStack Query, SWR, RTK Query, Apollo, Relay) or framework loaders / Server Components. Never copy it into useState |
| URL state | filters, sort, tab, page | Search params or route params, so it survives reload and can be shared |
| Form state | inputs before submit | Uncontrolled inputs plus FormData, or a form library for complex validation |
| Local UI state | open/closed, hover | useState in the lowest component that needs it |
| Shared client state | theme, current user, cart | Context for low-frequency values; a store with selectors (Zustand, Redux Toolkit, Jotai) for frequently changing ones |
| Derived values | filtered list, totals, validity | Compute during render; useMemo only if measured as expensive |
Lift state to the closest common parent of the components that need it, and no higher.
Rules
Derive, don't sync
// Bad: duplicated state kept in sync by an effect (extra render, can drift)
const [fullName, setFullName] = useState("");
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);
// Good
const fullName = `${first} ${last}`;Store IDs, not copies of objects: keep selectedId and look up the item, so edits to the list are never shadowed by a stale copy.
Effects are for external systems
Valid uses: subscriptions (WebSocket, browser events, matchMedia), third-party widgets and manual DOM integration, timers, and fetching data when no library or framework loader is available. Everything else (transforming data, reacting to a click, resetting state on a prop change, notifying a parent) belongs in render or in an event handler. See references/effects.md.
Every effect that starts something must stop it:
useEffect(() => {
const socket = connect(roomId);
socket.on("message", onMessage);
return () => socket.disconnect();
}, [roomId]);Strict Mode runs effects twice in development to expose missing cleanup. Fix the cleanup; don't remove Strict Mode.
Reset state with a key
// Remounts the form, clearing its state, whenever the user changes
<ProfileForm key={user.id} user={user} />Keys in lists
- Use a stable, unique ID from the data.
- Don't use the array index for lists that can reorder, insert or delete; state and focus attach to the wrong row.
- Never generate keys during render (
Math.random(),crypto.randomUUID()); every render remounts every row.
Controlled vs uncontrolled inputs
| Use | When |
Uncontrolled (defaultValue, read with FormData or a ref) | Most forms: values are only needed on submit |
Controlled (value + onChange) | The UI must react to every keystroke: live validation, formatting, dependent fields |
Never switch one input between controlled and uncontrolled (value={undefined} then a string). Initialize with "".
Composition before context
Pass components as children or slot props instead of threading props through layers that don't use them.
<Layout sidebar={<UserMenu user={user} />}>
<Dashboard user={user} />
</Layout>When context is right:
- Split contexts by how often they change (theme vs live cursor position). Every consumer re-renders when the value changes.
- Memoize object values so consumers don't re-render on every provider render:
const value = useMemo(() => ({ user, signOut }), [user, signOut]);
return <AuthContext value={value}>{children}</AuthContext>; // React 18: AuthContext.Provider- For high-frequency shared state, use a store with selectors instead of context.
Memoize only what you measured
Profile first with the React DevTools Profiler. Reach for useMemo, useCallback or memo when:
- a computation is measurably slow (filtering or sorting thousands of items on every keystroke),
- a memoized child or an effect dependency needs a stable reference.
Wrapping everything adds noise and hides real problems. For slow typing, try useDeferredValue or startTransition for the expensive part, and virtualize long lists.
Error boundaries and Suspense
- Put an error boundary at each route and around independent widgets so one failure doesn't blank the page. Boundaries are class components (
getDerivedStateFromError,componentDidCatch) or thereact-error-boundarypackage. - Boundaries do not catch errors in event handlers,
setTimeoutor other async code; handle those withtry/catchand show state. - Use
<Suspense fallback={...}>with data sources that support it (framework loaders,useSuspenseQuery,use(promise)in React 19). A promise passed tousemust come from a cache, a loader or a parent; one created during render is recreated on every render. - Place Suspense boundaries where a loading state makes sense to the user, not around every component.
Server and Client Components
Only applies in frameworks with React Server Components (for example the Next.js App Router).
- Components are Server Components by default.
"use client"at the top of a file makes it and everything it imports client code. Put the directive on small leaf components (a button, a chart), not on layouts. - Server Components cannot use state, effects, event handlers or browser APIs.
- Props passed from server to client must be serializable. Pass Server Components into Client Components as
children. - Import the
server-onlypackage in modules with secrets or DB access so they fail the build if imported into client code. - Functions marked
"use server"are public HTTP endpoints. Validate their input and check authorization inside every one.
Forms and actions (React 19)
import { useActionState } from "react";
async function subscribe(prevState: { error: string | null }, formData: FormData) {
const email = String(formData.get("email") ?? "");
if (!email.includes("@")) return { error: "Enter a valid email address." };
await api.subscribe(email);
return { error: null };
}
export function SubscribeForm() {
const [state, formAction, isPending] = useActionState(subscribe, { error: null });
return (
<form action={formAction}>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" required />
<button type="submit" disabled={isPending}>Subscribe</button>
{state.error && <p role="alert">{state.error}</p>}
</form>
);
}useFormStatus(fromreact-dom) only works in a component rendered inside the<form>.useOptimisticshows an expected result while the action runs and reverts on failure.- Validate on the server too; client checks are only for user experience.
Custom hooks
Extract a hook to share logic, not state: each call gets its own state. Name it useSomething, call hooks unconditionally at the top level, and return the smallest useful API.
Testing
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("adds a todo", async () => {
const user = userEvent.setup();
render(<Todos />);
await user.type(screen.getByRole("textbox", { name: /new todo/i }), "Buy milk");
await user.click(screen.getByRole("button", { name: /add/i }));
expect(screen.getByRole("listitem")).toHaveTextContent("Buy milk");
});- Query in this order:
getByRole(withname),getByLabelText,getByText, andgetByTestIdonly as a last resort. Role queries also catch accessibility bugs. - Use
findBy*for content that appears asynchronously; don't add manual waits. - Mock the network at the boundary (for example MSW), not the component's own hooks.
- Test what the user sees and does, not state variables or hook internals.
Anti-patterns
- Effect chains: one effect sets state that triggers another. Compute in render or do it all in the event handler.
- Fetching in
useEffectwithout cleanup: race conditions show stale results. Use a data library, or handle races as shown in the reference. - Server data copied into
useState: it goes stale. Read it from the cache. - Objects or functions created in render used as effect dependencies: the effect runs every render. Move them inside the effect or outside the component.
- Index or random keys on dynamic lists.
- Defining a component inside another component: it remounts on every render and loses state and focus.
- One giant context holding everything.
"use client"at the root layout: it turns the whole tree into client code.- Suppressing
exhaustive-deps: it hides stale closures.
Done when
- No effect exists that only transforms data or responds to a user event.
- Every effect that subscribes, connects or starts a timer has cleanup.
- Server state lives in a data library or framework loader, URL state lives in the URL.
- Hooks lint passes with no suppressed warnings; tests query by role.
- Any memoization added is backed by a profile or a stable-reference requirement.
References
- references/effects.md — read when writing, reviewing or debugging any
useEffect, or deciding between an effect, an event handler, render-time computation oruseSyncExternalStore.
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.

