renderpx
Theme: auto

Render Props vs Hooks

Share behavior (e.g. mouse position, subscription) between components. Render props pass a function that receives state; hooks return state directly. Prefer hooks for most logic; keep render props when the consumer must supply the UI structure.

The problem I keep seeing

You have logic that multiple components need—mouse position, window size, a subscription—and you don’t want to duplicate the effect and state in every component. You need a way to reuse the behavior and let each consumer render its own UI. Two common patterns: render props (a component that calls a function with the value) and custom hooks (a function that returns the value).

Naive approach

Copy the same useState + useEffect into every component that needs the behavior. It works but duplicates code and is hard to change consistently.

tsx
Loading...

First improvement

Extract the logic into a component that accepts a render prop (or children as a function): it holds the state and effect, and calls the function with the current value. The consumer decides what to render. One place for the behavior; flexible output.

tsx
Loading...

Remaining issues

  • Composition: Combining two render-props components leads to nested functions (callback hell). Hooks compose flat: useA(); useB().
  • Hooks rules: You can’t call hooks conditionally or inside a render prop; the hook must be called in the component that uses the value. So for “reusable logic,” a hook is often simpler.
  • When render props still help: When the parent needs to control the exact structure (e.g. compound components, or when the child needs to be passed to a specific slot).

Production pattern

For most reusable behavior, use a custom hook: it returns the value (and any setters) and the component uses it directly. No wrapper component, no extra nesting, and you can use multiple hooks in one component. Keep render props when the API is “here’s the value, you render the tree” and the consumer must supply the structure (e.g. a data provider that wraps children with a function). Many libraries that used to be render-props-only now expose hooks (e.g. React Query’s useQuery); prefer the hook when both exist.

tsx
Loading...

When I use this

  • Hooks: Reusable state + effect (mouse, resize, subscription, form state). Use whenever the consumer just needs a value.
  • Render props: When the provider must control where the rendered output goes (e.g. “render this list using my layout”) or when building compound components that accept a function as child.

Gotchas

  • Don’t call hooks inside the render prop: The render prop runs in the parent’s render; hooks must run in the same order in the component that uses them. Put the hook in the component that receives the value.
  • children as function: <MouseTracker>{({ x, y }) => <div>{x}</div>}</MouseTracker> is a common variant; same idea as render prop.

Compound Components → · Component Composition → · All patterns