react-patterns
Skill0 starsChanged 5 months agoMITView on GitHub
Applies performance-minded React and Next.js patterns. Use when writing, reviewing, or refactoring React/Next.js code — components, Server/Client boundaries, data fetching, rendering, bundle size, re-renders, forms, or streaming.
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
- reddygopalkrishna/developer-agent-skillstheir 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
Used with
Projects with it also use typescript-patterns, typescript-strict and 8 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.
cohen-lielSkilltypescript-patterns
TypeScript best practices and patterns. Use when writing TypeScript code, defining types, working with generics, or converting JavaScript to TypeScript.
Also in 3 projects that use this
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
LangChainSkilldatabase-migrations
Database migration patterns and schema versioning
Also in 4 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 repository68 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- .cursor/rules/engineering-skills.mdcCursor rule
- marketing-routerSkill
- accessibilitySkill
- agent-guardrailsSkill
- agent-memorySkill
- agent-verificationSkill
- autonomous-loopsSkill
- browser-checksSkill
- caching-strategySkill
- context-curationSkill
- contract-testingSkill
- data-modelingSkill
- decision-docsSkill
- dependency-hygieneSkill
- design-handoffSkill
- e2e-testingSkill
- experimentationSkill
- fault-recoverySkill
- feature-flagsSkill
- finops-budgetSkill
- git-flowSkill
- hardeningSkill
- i18n-l10nSkill
- idea-shapingSkill
- incident-responseSkill
- incremental-deliverySkill
- interface-designSkill
- launch-campaignSkill
- launch-readinessSkill
- llm-feature-engineeringSkill
- long-running-agentsSkill
- community-engagementSkill
- content-marketingSkill
- email-nurtureSkill
- growth-strategySkill
- paid-adsSkill
- referral-loopSkill
- seo-growthSkill
- social-distributionSkill
- micro-interactionsSkill
- migration-pathSkill
- mobile-patternsSkill
- observabilitySkill
- orchestrated-deliverySkill
- parallel-subagentsSkill
- perf-budgetSkill
- pipeline-opsSkill
- product-briefSkill
- product-discoverySkill
- product-groomingSkill
- resilienceSkill
- review-gateSkill
- simplifySkill
- skill-creatorSkill
- skill-harvestSkill
- skill-routerSkill
- source-firstSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
23 rules. 0 are common, 10 are distinctive, and 13 are very specific to this project.
0
Common
Most of the 34 edited versions we read keep these.
10
Distinctive
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
Run the react patterns checklist alongside this process for a quick pass.
- Distinctive: line 128
Every kilobyte of JavaScript is downloaded, parsed, and executed before the page is interactive.
- Distinctive: line 130
Import the specific module you need — avoid barrel/index files that drag unrelated code into the chunk.
- Distinctive: line 136
Keep import paths statically analyzable so the bundler can tree-shake.
- Very specific: line 156
Use a cross-request cache ([[caching-strategy]]) — fetch with next: { revalidate }, cache tags, or framework cache helpers — for data that …
- Very specific: line 165
Prefer notFound() / redirect() at the data layer over fetching then conditionally rendering empty states for missing resources — fail fast …
- Very specific: line 181
Use a caching/deduping data layer (SWR, TanStack Query) so concurrent components asking for the same key share one request.
- Distinctive: line 195
Do not subscribe a component to state it only reads inside a callback — read lazily (ref or functional updater) instead.
- Very specific: line 198
Prefer primitive dependencies in useEffect/useMemo.
- Distinctive: line 201
Do not define components inside other components — each render creates a new type and remounts the subtree.
- Very specific: line 205
memo/useMemo/useCallback: only on measured-expensive paths — they add comparison cost. - Very specific: line 231
Use stable, unique keys from data (id), not array index, when items reorder, insert, or delete.
- Very specific: line 233
Use resource hints (preload/preconnect) for critical assets.
- Very specific: line 234
Avoid hydration mismatch: don't render client-only values (random IDs, Date.now(), window size) during SSR — gate on mount or use …
- Distinctive: line 242
Prefer Server Actions for mutations when using the App Router — progressive enhancement works without JS.
- Very specific: line 246
Call revalidateTag / revalidatePath (or equivalent) on every successful write that affects cached reads — missing invalidation is a …
- Very specific: line 248
Use useOptimistic for instant feedback on adds/toggles/deletes.
- Distinctive: line 274
Always clean up subscriptions, timers, and listeners in the effect return.
- Very specific: line 284
Use route-level loading.js for whole-segment defaults.
- Very specific: line 301
Prefer immutable helpers (toSorted, flatMap) for clarity without extra passes.
- Distinctive: line 305
Keep frequently-changing callbacks stable with refs so memoized children don't re-render on every parent render.
- Distinctive: line 307
Run one-time app initialization exactly once per load (module scope or dedicated bootstrap), not per mount in every root component.
- Very specific: line 311
When the React Compiler is enabled, manual memo/useMemo may be redundant — still apply architectural patterns (boundaries, waterfalls, …
“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
Practical rules for writing fast, predictable React and Next.js code. Patterns are grouped by impact so
when you have to choose what to fix first, start at the top. Most slowness in React apps is
architectural — waterfalls, wrong boundaries, bundle bloat — not missing memo on a leaf component.
This skill describes patterns and principles in its own words. For deeper background, the React and Next.js docs are the source of truth (see Further Reading). Treat the examples here as illustrations, not copy-paste snippets.
Run the react patterns checklist alongside this process for a quick pass. Pair with [[perf-budget]] to measure before optimizing, [[browser-checks]] to verify in a real browser, [[ui-craft]] for interaction quality, [[micro-interactions]] for press states and view transitions, [[caching-strategy]] when adding cross-request caches, and [[accessibility]] when patterns affect focus or announcements.
When to Use
- Writing a new component, page, route handler, or server action
- Choosing Server vs Client Components or moving data fetching
- Adding or restructuring data fetching (RSC, client cache, mutations)
- Reviewing a PR for performance, re-render, or hydration problems
- A page feels slow: slow first paint, janky typing, large JS bundle, unnecessary refetching, or layout shift
- Refactoring legacy class/effect-heavy components or Pages Router code
- Implementing forms, optimistic UI, or streaming loading states
Skip as the primary skill when the change has no React/Next surface (pure SQL, CLI, unrelated backend). Not a substitute for [[perf-budget]] — profile and set a budget before micro-optimizing.
Process
Work top-down. The earlier categories almost always matter more than the later ones.
0. Place the Server/Client boundary correctly
In the App Router, Server Components are the default. A Client Component ('use client') is needed
only when the tree uses interactivity, browser-only APIs, or state/effect hooks.
| Put on the server | Keep on the client |
| Data fetching, secrets, DB/API calls | onClick, onChange, drag-and-drop |
| Large dependencies that never need the browser | useState, useEffect, most hooks |
| Static or slow-changing markup | Browser APIs (localStorage, window, observers) |
Streaming via Suspense | Third-party widgets that require the DOM |
Rules:
- Push
'use client'to the leaves — a small interactive island, not the whole page wrapper. - Never import server-only code into a Client Component — no DB clients, env secrets, or
server-onlymodules in client bundles. - Pass serializable props across the boundary — functions, class instances, and Dates don't serialize cleanly; pass IDs and plain data, fetch or mutate on the correct side.
- Colocate fetching with the component that consumes the data — don't fetch in a parent and drill props through five layers when a Server Component child can fetch directly.
// Prefer: page shell is server; button is a small client leaf
export default async function Page() {
const data = await getData(); // server
return (
<>
<StaticSummary data={data} />
<LikeButton itemId={data.id} /> {/* 'use client' inside */}
</>
);
}1. Remove request waterfalls (highest impact)
A waterfall is any sequence where request B only starts after request A finishes, even though B did not need A's result. Each hop adds a full round-trip of latency.
- Kick off independent async work together and await the group — prefer
Promise.allfor fully independent work. - Cheap synchronous checks (auth role, feature flag in memory, input validation) belong before any
await, so you fail fast without paying for a network call. - In route handlers and server actions, start promises early and only
awaitat the point you consume the value. - In layouts/pages, don't
awaitparent data before starting child fetches that don't depend on it — parallelize across the tree with separate async Server Components wrapped inSuspense. - On the server, lean on streaming: wrap slow, non-critical regions in
Suspenseso the shell ships immediately and the slow part fills in later.
// Avoid: sequential, even though the two reads are unrelated
const user = await getUser(id);
const posts = await getPosts(id);
// Prefer: both in flight at once
const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);// Avoid: child waits for parent await before its fetch starts
export default async function Page() {
const header = await getHeader();
return (
<>
<Header data={header} />
<SlowFeed /> {/* async inside, but only mounts after header resolves */}
</>
);
}
// Prefer: stream independent regions
export default function Page() {
return (
<>
<Suspense fallback={<HeaderSkeleton />}>
<Header />
</Suspense>
<Suspense fallback={<FeedSkeleton />}>
<SlowFeed />
</Suspense>
</>
);
}2. Keep the bundle small (highest impact)
Every kilobyte of JavaScript is downloaded, parsed, and executed before the page is interactive.
- Import the specific module you need — avoid barrel/index files that drag unrelated code into the chunk.
- Load heavy, below-the-fold, or rarely-used UI with
next/dynamicorReact.lazy; use{ ssr: false }only when the component truly cannot render on the server. - Defer non-essential third-party scripts (analytics, chat widgets) until after hydration or first
interaction (
next/scriptwithstrategy="lazyOnload"or on user intent). - Keep import paths statically analyzable so the bundler can tree-shake; avoid dynamic string paths the bundler cannot follow.
- Preload on intent — prefetch routes with
<Link prefetch>(default in viewport) or on hover/focus for heavy navigations. - Audit with the bundle analyzer ([[perf-budget]]) — assumptions about tree-shaking are often wrong.
// Avoid: pulls entire icon library
import { Icon } from '@/components/icons';
// Prefer: direct import
import { CheckIcon } from '@/components/icons/CheckIcon';3. Server-side performance (high)
- Authenticate server actions exactly like API routes — they are public entry points, not trusted internals ([[hardening]]).
- Deduplicate per-request reads with
React.cache()(or equivalent request-scoped memo) so the same data is not fetched twice in one render tree. - Use a cross-request cache ([[caching-strategy]]) —
fetchwithnext: { revalidate }, cache tags, or framework cache helpers — for data that is expensive and changes slowly. Document invalidation (revalidateTag,revalidatePath) on every write path. - Send only what the Client Component actually needs as props — large serialized payloads cost bandwidth and hydration time.
- Never hold request-specific data in module-level mutable variables — modules are shared across requests and leak data between users.
- Hoist static I/O (font files, logos, config read once) to module scope so it runs once, not per request.
- Prefer
notFound()/redirect()at the data layer over fetching then conditionally rendering empty states for missing resources — fail fast on the server.
import { cache } from 'react';
// Deduped within a single request/render pass
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});4. Client-side data fetching (medium-high)
- Don't fetch in
useEffectwhat a Server Component can fetch — client fetching is for live updates, user-specific post-hydration data, or polling — not the initial page payload. - Use a caching/deduping data layer (SWR, TanStack Query) so concurrent components asking for the
same key share one request; configure
staleTime/gcTimeexplicitly. - Register global listeners once (resize, scroll, storage) and fan out — not one listener per component instance.
- Mark scroll/touch listeners
passivewhen you do not callpreventDefault, so scrolling stays smooth. - Version and trim anything persisted to
localStorage; validate shape on read — corrupt storage should not crash the tree. - Cancel or ignore stale responses when inputs change (AbortController, query library built-ins).
5. State colocation and re-render control (medium)
- Colocate state with the component that uses it — lift only when siblings truly need to share it. URL/search params are ideal for shareable, bookmarkable UI state.
- Do not subscribe a component to state it only reads inside a callback — read lazily (ref or functional updater) instead.
- Derive values during render; do not mirror props into state via an effect.
- Prefer primitive dependencies in
useEffect/useMemo; objects and arrays recreated every render defeat comparison and retrigger effects. - Initialize expensive
useStatewith a function so the work runs once, not on every render. - Do not define components inside other components — each render creates a new type and remounts the subtree.
- Reach for
useTransition/useDeferredValueto keep input responsive while heavy renders catch up — prefer these over manual debounce for render-bound work. memo/useMemo/useCallbackonly on measured-expensive paths — they add comparison cost; don't wrap every primitive prop.- Split Context by concern or pass stable selectors — one big context value re-renders every consumer on any field change.
// Avoid: recomputed every render
const [items] = useState(buildExpensiveList());
// Prefer: lazy initializer runs once
const [items] = useState(() => buildExpensiveList());// Avoid: effect mirrors props → state
useEffect(() => { setCount(initialCount); }, [initialCount]);
// Prefer: derive, or reset with a key on the parent
const count = deriveCount(props);
// or <Counter key={userId} initialCount={initialCount} />6. Rendering and hydration (medium)
- For long lists, use
content-visibility: autofor offscreen rows and virtualize when the list is genuinely large (thousands) — measure before adding virtualization complexity. - Use stable, unique keys from data (
id), not array index, when items reorder, insert, or delete. - Hoist static JSX out of the render path so it is not recreated each render.
- Use resource hints (
preload/preconnect) for critical assets;defer/asyncon scripts. - Avoid hydration mismatch: don't render client-only values (random IDs,
Date.now(),windowsize) during SSR — gate on mount or usesuppressHydrationWarningonly for known-safe text (e.g. timestamps). - Loading UI must reserve space — skeletons with fixed dimensions beat spinners that collapse and cause layout shift ([[ui-craft]], [[browser-checks]]).
7. Forms, mutations, and optimistic UI (medium)
- Prefer Server Actions for mutations when using the App Router — progressive enhancement works without JS; still validate and authorize server-side.
- Return structured errors from actions (field-level + form-level) and surface them with
useActionState— don't rely on thrown errors alone for expected validation failures. - Call
revalidateTag/revalidatePath(or equivalent) on every successful write that affects cached reads — missing invalidation is a stale-data bug ([[caching-strategy]]). - Use
useOptimisticfor instant feedback on adds/toggles/deletes; reconcile when the server responds — roll back on failure with a clear message. - Disable double-submit — pending state on the submit button or action, not just hope.
'use client';
async function action(prevState, formData) {
'use server';
// validate, authorize, mutate, revalidateTag('items')
return { ok: true };
}8. Effects — synchronization only (medium)
Effects are for syncing with external systems (DOM subscriptions, network bridges, non-React widgets) — not for transforming data or responding to user events.
| Use an effect for | Don't use an effect for |
| Subscribing to external store/event | Deriving state from props/state |
| Integrating non-React library | Handling clicks/submits (event handlers) |
| Syncing with browser API after mount | Fetching data RSC can fetch on first paint |
| Logging/analytics side channel | Resetting state when props change (use key) |
- Always clean up subscriptions, timers, and listeners in the effect return.
- For logic that must read latest props/state inside an effect without re-subscribing, use the
Effect Event pattern (
useEffectEventin React 19+) — don't omit dependencies that should trigger re-sync. - Strict Mode double-mount in dev is intentional — effects must be idempotent and cleanup-safe.
9. Suspense, errors, and loading boundaries (medium)
- Wrap async Server Components in
Suspensewith a meaningful fallback — the fallback is what users see during streaming; make it structurally similar to the final UI. - Use route-level
loading.jsfor whole-segment defaults; use inlineSuspensewhen only part of the page is slow. error.js(Client Component) catches render errors in a segment — pair with a recovery action (retry, go home); log the error server-side ([[observability]]).- Error boundaries don't catch event-handler errors or server action failures — handle those in the action/handler and return user-visible errors.
- Nested Suspense boundaries let fast regions paint first — don't wrap the entire page in one boundary waiting for the slowest query.
10. JavaScript micro-patterns (low-medium)
These matter in hot paths and large loops — measure before micro-optimizing ([[perf-budget]]).
- Build a
Map/Setonce for repeated lookups instead of scanning an array each time. - Hoist
RegExpand other constant constructions out of loops. - Combine multiple
filter/mappasses into a single loop when the data is large. - Return early to skip work; check
lengthbefore expensive comparisons. - Prefer immutable helpers (
toSorted,flatMap) for clarity without extra passes.
11. Advanced (low, situational)
- Keep frequently-changing callbacks stable with refs so memoized children don't re-render on every parent render.
- Run one-time app initialization exactly once per load (module scope or dedicated bootstrap), not per mount in every root component.
- Be careful not to feed Effect Event results into effect dependency arrays — they are not dependencies.
- When the React Compiler is enabled, manual
memo/useMemomay be redundant — still apply architectural patterns (boundaries, waterfalls, bundle size); verify with profiler, don't delete all memoization blindly.
Common Rationalizations
- "It's only one extra await." — One serial await is one extra round-trip on the critical path; they compound fast across layout, page, and child components.
- "The whole library tree-shakes anyway." — Barrel imports frequently defeat tree-shaking; verify with the bundle analyzer, don't assume.
- "Memoizing everything is safer." — Needless
memo/useMemoadds comparison cost and complexity; memoize measured-expensive work, not primitives. - "We'll optimize later." — Waterfalls and bundle bloat are architectural; they get harder to remove the longer they live.
- "This page needs
'use client'at the top." — One hook or click handler rarely justifies shipping the entire page as client JS; split into a leaf. - "I'll fetch in
useEffect— it's simpler." — You pay with an extra client round-trip, loading flash, and no SSR for that data. - "Effects keep things in sync." — Most "sync" effects are derived state or event logic in disguise; remove the effect and the bug class goes away.
- "Index as key is fine here." — Fine until insert, delete, or reorder — then state lands on the wrong row and bugs are subtle.
Red Flags
- A chain of
awaits where later calls don't use earlier results 'use client'on a page/layout that is mostly static data and markup- Server-only imports reachable from a Client Component bundle
import { X } from 'big-lib'for a single helper, or dynamic import paths the bundler can't trace- Module-level
letstoring per-user/request data on the server - An effect whose only job is copying props into state or transforming already-available data
- A component defined inside another component's body
useEffect(() => { fetch(...) }, [])for initial page data a Server Component could fetch- Persisted client state with no version field or shape validation
- Mutations with no cache revalidation or tag invalidation story
- Hydration warnings in the console ([[browser-checks]])
- Loading spinners with no reserved height — content jumps when data arrives
- One Context provider holding unrelated fast-changing and slow-changing state together
Verification
- Server/Client boundary is at the leaves; no server secrets in client bundles
- Independent async work runs in parallel; cheap checks precede awaits; slow regions stream via Suspense
- Bundle impact checked (analyzer); heavy/rare code is lazy-loaded; barrel imports avoided
- Server actions authenticated; no module-level mutable request state; writes invalidate cached reads
- Per-request reads deduplicated; Client Components receive minimal serializable props
- No prop→state mirroring effects; expensive
useStateuses a lazy initializer - Effects are for external sync only — no fetch-on-mount for data RSC can provide
- Forms/mutations handle pending, error, and double-submit; optimistic UI rolls back on failure
- Loading/error boundaries in place; fallbacks match layout to avoid shift
- Typing/interaction stays responsive under load (transitions where needed)
- Measured before and after ([[perf-budget]]) — the change is justified by numbers, not vibes
- Checklist in react patterns checklist completed for the change
Further Reading
- React docs — https://react.dev
- Next.js App Router — https://nextjs.org/docs/app
- React Server Components — https://react.dev/reference/rsc/server-components
- SWR — https://swr.vercel.app
- TanStack Query — https://tanstack.com/query
These patterns reflect widely-documented React/Next.js performance and architecture guidance. Written for this repo; not a reproduction of any third-party document.
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.

