Back to React
React
easy
mid

What is the render props pattern in React and when do you use it?

Render props: a component takes a function as a child (or prop) and calls it with internal state. `<Mouse>{({x, y}) => <p>{x}</p>}</Mouse>` — the consumer decides what to render. Pre-hooks pattern for sharing logic. Mostly replaced by custom hooks; still useful for inversion of control (forms, drag, headless UI).

3 min read·~6 min to think through

The pattern

tsx
function Mouse({ children }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handler);
    return () => window.removeEventListener('mousemove', handler);
  }, []);
  return children(pos);
}

<Mouse>{({ x, y }) => <p>{x}, {y}</p>}</Mouse>

children is a function; the parent passes state, the consumer renders.

Why it existed

Pre-hooks, sharing logic between components required HOCs or render props. Both:

  • HOC: withMouse(Component) wraps and injects.
  • Render prop: <Mouse>{fn}</Mouse> exposes state via callback.

Why hooks mostly replaced it

tsx
const pos = useMouse();

Cleaner; no wrapper components; no "render prop hell."

Where render props still shine

Headless components / inversion of control

tsx
<Combobox>
  {({ input, list }) => (
    <>
      <CustomInput {...input.props} />
      <CustomList {...list.props}>{list.options.map(...)}</CustomList>
    </>
  )}
</Combobox>

The component owns state + ARIA wiring; consumer owns UI. Downshift, react-hook-form's <Controller>, Radix examples.

Form fields with complex layouts

tsx
<Field name="email">
  {({ value, onChange, error }) => <CustomInput ... />}
</Field>

Drag / gesture libs

tsx
<Draggable>
  {({ x, y, dragging }) => <div style={{ transform: ... }} />}
</Draggable>

Tradeoffs

Render propsHooks
Inversion of control via JSXInversion via function call
Visible in component tree (debugger sees it)Invisible in tree
Can scope state to a JSX subtreeHook state per component
'Wrapper hell' if nestedStack of useX(), readable

Common mistakes

  • Re-creating the render function inline per render → memoized child remounts.
  • Render-prop inside another render-prop → indentation hell.
  • Doing too much in the render-prop component when a hook + small primitive would do.

Modern recipe

For new code: prefer hooks for logic and primitives for UI. Render props for headless-component patterns where consumer control is the whole point.

Interview framing

"Render props: a component takes a function (often as children) and calls it with internal state — consumer decides what to render. Pre-hooks pattern for sharing logic; mostly replaced by custom hooks now. Still useful for headless components and inversion of control — Downshift, react-hook-form's Controller, drag libraries. New code defaults to hooks; reach for render props when the consumer needs to fully control the rendered output while you own the state + a11y wiring."

Follow-up questions

  • Compare render props vs custom hooks.
  • When have you used a render prop recently?
  • What's a HOC and how does it compare?

Common mistakes

  • Render-prop hell from nesting.
  • Re-creating render function inline.

Performance considerations

  • Each render creates a new render function — memoize or accept the re-render cost.

Edge cases

  • Render prop + React.memo'd parent — function reference changes per render.
  • Using both children-as-function and named children.

Real-world examples

  • Downshift, react-hook-form Controller, Formik FieldArray, framer-motion's render prop forms.

Senior engineer discussion

Seniors reach for hooks by default; render props for headless components where consumers want full control.

Related questions