Skip to content
Community content. Review instructions before giving them to an AI agent — treat modules like open-source code.

React Conventions

Component and hook conventions for React apps: composition over inheritance, honest dependency arrays, state colocation, derived state, controlled forms, key discipline, and measured memoization.

Mby @markdownersPublished August 21, 2026 · ~4 min read

0 downloads · Used by 0 stacks

Prefer composing small components over building inheritance-like abstractions, and treat every piece of state as something that must justify why it exists where it lives — most React bugs come from state that's stale, duplicated, or living one level too high or too low.

Component composition

  • Build UI by composing small, focused components (children/render props/slots) rather than parameterizing one large component with booleans and conditionals — a component with five boolean props hiding six different layouts is harder to reason about than five small components.
  • Never reach for a class hierarchy or a shared base component to share behavior — extract a hook or a wrapper component instead; React's composition model doesn't need inheritance.
  • Keep components pure with respect to props/state during render — no mutating props, no side effects in the render body itself; side effects belong in event handlers or effects.

Hooks: honest dependency arrays

  • Include every value the effect/callback/memo actually reads in its dependency array — never suppress the linter rule to silence a warning; a missing dependency is a stale-closure bug waiting to happen, not a lint annoyance.
  • If a dependency array must exclude a value on purpose, restructure the code (move the value inside the effect, use a ref for a value you intentionally don't want to react to) rather than lying to the array.
  • Prefer the functional form of a state setter (setCount(c => c + 1)) when the new value depends on the previous one, so the update doesn't need to be a dependency at all.

Custom hooks for shared logic

  • Extract shared stateful logic (data fetching, subscriptions, form state) into a custom hook the moment it's needed in a second component — don't wait for a third duplication, and don't copy-paste effect logic between components.
  • Name custom hooks for what they provide (useDebouncedValue, useMediaQuery), not for the component that first needed them, so the hook reads as reusable infrastructure.

State colocation and derived state

  • Put state in the lowest component that needs it; lift it only as far as the nearest common ancestor of the components that actually use it — state living in a top-level provider "just in case" causes unnecessary re-renders across the tree.
  • Never store a value in state if it can be computed from existing props/state during render (a filtered list, a formatted string, a total) — compute it inline or with useMemo if the computation is expensive; storing it invites the two copies to drift out of sync.
  • Reset state by changing a component's key (remounting) instead of writing an effect that manually resets fields when a prop like an item ID changes.

Controlled forms

  • Prefer controlled inputs (value + onChange bound to state) for any field the app validates, formats, or submits programmatically; use uncontrolled inputs with refs only for simple, single-shot cases (a file input, a form library already handling state).
  • Validate on submit and on blur, not on every keystroke, unless the UI explicitly needs live feedback — keystroke-level validation that also blocks typing is a common source of frustrating input bugs.

List rendering and key discipline

  • Use a stable, unique identifier from the data (a database ID) as the key for list items — never the array index, except for a genuinely static list that never reorders, filters, or has items inserted/removed.
  • An index key on a dynamic list causes state to attach to the wrong item after a reorder or deletion (wrong input focus, wrong text mid-edit) — treat this as a correctness bug, not a lint nitpick.

Memoization only when measured

  • Do not wrap every component in memo or every value in useMemo/useCallback by default — memoization has its own cost (comparison, memory) and adds noise; reach for it after profiling shows a specific re-render is expensive and frequent.
  • When memoizing a callback passed to a memoized child, memoize the callback too (useCallback) — otherwise a new function identity on every render defeats the child's memo entirely.
  • Prefer moving state down (colocation) or splitting a component to naturally reduce re-render scope before reaching for a memoization API to paper over unnecessary re-renders.
Badge

Link back to this module from your own README.

Get it on Markdowners
[![Get it on Markdowners](https://markdowners.com/mdstack-badge.svg)](https://markdowners.com/m/markdowners/react-conventions)

Comments (0)

Sign in to comment. Sign in

No comments yet. Be the first to add one.

Discussions about this module

No discussions about this module yet.

Start a discussion