React Patterns Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in this section.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
children for multi-region layouts loses placement control; expose named ReactNode props - ({ header, sidebar, footer }: { header: ReactNode; sidebar: ReactNode; footer: ReactNode }) - so consumers can fill each slot independently and you keep TypeScript on their side.ReactNode accepts strings/null/arrays (best for slots and children), ReactElement narrows to a single JSX element, and ComponentType<P> is a component you can instantiate - choose intentionally because they type different composition styles.Root.Item, Root.Trigger, and Root.Content work at any JSX depth - guard the consumer hook with a thrown error when used outside the root instead of returning a silent null.createContext and useState, so the root file must be a Client Component in Next.js - and use two-level context (AccordionContext + per-item ItemContext) to scope identity to each item.useReducer's dispatch is already React-stable and needs no extra memoization.value on every parent render defeats memoization and re-renders every consumer; wrap the value in useMemo keyed on the actual state, or split contexts so updater-only consumers are immune.value !== undefined and fall back to internal state - const [inner, setInner] = useState(defaultValue); const val = value !== undefined ? value : inner - wrap the decision in a reusable useControllableState hook and never switch modes after mount.onChange that sets state freezes the input because the DOM value lags behind the user; debounce side effects (search, autosave) while always updating controlled state synchronously.getDerivedStateFromError (pure render-phase state derivation) for the fallback switch and componentDidCatch (post-commit) for logging to Sentry; there is no hook equivalent, so use a class or react-error-boundary.resetKeys so the boundary auto-recovers when a route param changes, and put a key on the boundary's children so clearing hasError forces a remount - simply resetting state leaves the subtree in its corrupted state.try/catch, and in Next.js rely on the route-level error.tsx convention with error.digest for server errors.withAuth(Component) inside another component's body creates a brand-new component type every render and destroys state; apply HOCs once at module scope - const ProtectedPage = withAuth(DashboardPage) - and always set a meaningful displayName.React.forwardRef internally and copy statics with hoist-non-react-statics - otherwise consumers lose imperative handles and attached constants.memo/useMemo/useCallback adds comparison overhead that can slow fast components; use React DevTools Profiler to identify real hot spots and remember inline onClick={() => …} or style={{…}} defeats memo via fresh references.useTransition when you own the state setter and want to mark the update non-urgent; use useDeferredValue to lag a value you receive as a prop, and compare it with the current value to show a subtle stale indicator.memo - reach for react-virtual/react-window or CSS content-visibility: auto, and keep stable keys (IDs, not indices) so rows are not remounted on scroll.stopPropagation when you need a clean break, and trap focus plus save/restore it for real modals.document.body crashes server rendering, so gate the portal with a useEffect-set mounted flag - const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []) - or mark the component "use client" in Next.js, and create a dedicated container node instead of piling up empty divs on body.children={(state) => <X />} creates a new function reference on every render and breaks React.memo on the parent; hoist the function or accept that memo will not apply here, and remember hooks cannot be called inside a render-prop function.<button {...triggerProps}>Open</button> <div {...contentProps}>…</div> - consumers cannot forget accessibility wiring because it ships with the collection.isLoading/isError/hasData boolean soup with a single status tagged union so impossible combinations are unrepresentable; each state carries only the data valid for that state.return state for unknown events so unhandled transitions don't produce undefined.use(promise) treats a new reference as a new pending request; hoist promise creation into a parent, a cache, or an event handler.useTransition when replacing Suspense-driven UI so fast fetches don't flash a skeleton, and nest boundaries so secondary content streams in after the critical shell.Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥