skillv1.0.0
react-modern
Modern React 18+ patterns: hooks, Suspense, Server Components, ref semantics
reactjavascripttypescriptfrontend
Install
$npx autoskills --items react-modern
Or scan + install everything matching your stack with npx autoskills.
Source
View on GitHubModern React
React 18+ is a different language than React 16. If you're applying hooks-era advice, you're behind. This skill covers the patterns that actually survived.
Hooks: what's real, what's cargo cult
useEffectis a synchronization primitive, not a "lifecycle". Reach for it only when you need to sync with an external system (DOM, subscription, network). Computing derived state from props belongs in render oruseMemo, not effects.- No state-in-effect.
useEffect(() => setX(deriveFromProps(p)), [p])is always wrong. Computexinline. - Refs are an escape hatch. If you find yourself reading
.currentto make rendering work, you're fighting React. Refs are for DOM nodes, mutable values that don't trigger re-render, and integrations with non-React code. useMemo/useCallbackare not free. Profile before adding. Reference-stability matters mainly for child memoization, not for hot loops.
Server Components (Next.js / RSC frameworks)
- Default to Server Components. Mark
'use client'only on files that need state, effects, or browser APIs. - Don't pass functions across the server/client boundary. Functions can't serialize. Pass primitives, plain objects, or Server Actions.
- Server Components can be async.
async function Page()andawait db.query(...)directly inside the component is the canonical pattern โ nouseEffect+useStateloaders. - Streaming is opt-in via Suspense. Wrap slow data with
<Suspense fallback={...}>to send the rest of the tree immediately.
Suspense and data fetching
- Suspense boundaries are render boundaries. Place them where you want a loading state, not at the data-fetch site.
use(promise)is the data hook for RSC and forthcoming use in client. Don't writeuseEffect+useStatefor fetches when a higher-level framework gives you Suspense-aware fetching.
State
- Local state first, then context, then a library. Reaching for Zustand/Redux on day one is overkill.
- Don't put server state in client state libraries. TanStack Query / RTK Query / SWR are for server state. Zustand is for UI state. Mixing them produces stale-cache bugs.
Forms
- Uncontrolled by default.
<form action={serverAction}>withFormDatareads in the action. Controlled inputs only when you need real-time validation/derivation. useActionState(formerlyuseFormState) is the canonical progress/error hook for Server Actions.
Rendering and keys
- Keys are identity, not index. Using array index as
keycorrupts state when the list reorders. - List children with stable IDs always. If your data doesn't have IDs, generate them once at the source, not at render time.
What to delete from old codebases
React.FCโ adds implicit children, breaks generics. Use explicit prop interfaces.componentDidMount/componentWillUnmountโ class lifecycle is over.forwardRefwrapper โ React 19 acceptsrefas a regular prop. Strip the wrapper.- Most
useCallback/useMemoโ unless you measured them helping.
Quick checklist for new components
- Server Component by default โ add
'use client'only if needed. - Async-await data inline (no effect-based fetching).
- State is local until proven otherwise.
- Stable keys for lists.
- Form actions over controlled inputs unless you need real-time state.
- Suspense boundaries where you want loading UI.