5 min read
0%

Render Props Pattern

Back to Blog
Render Props Pattern

Render Props Pattern

Render props is a technique where a component receives a function as a prop and calls it to determine what to render. It enables behavior sharing without HOC wrapper nesting.

Basic Shape

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  return (
    <div onMouseMove={e => setPosition({ x: e.clientX, y: e.clientY })}>
      {render(position)}
    </div>
  );
}

// Usage
<MouseTracker render={({ x, y }) => (
  <p>Mouse at {x}, {y}</p>
)} />

The consumer controls what renders; the provider controls when and with what data.

Children as Function

The children prop can be the render function, which reads more naturally:

function MouseTracker({ children }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  return (
    <div onMouseMove={e => setPosition({ x: e.clientX, y: e.clientY })}>
      {children(position)}
    </div>
  );
}

<MouseTracker>
  {({ x, y }) => <Cursor x={x} y={y} />}
</MouseTracker>

Common Use Cases

Virtualization:

<VirtualList
  items={rows}
  renderItem={(item) => <Row key={item.id} data={item} />}
/>

Form fields (Formik-style):

<Field name="email">
  {({ field, meta }) => (
    <div>
      <input {...field} />
      {meta.error && <span>{meta.error}</span>}
    </div>
  )}
</Field>

Toggle:

function Toggle({ children }) {
  const [on, setOn] = useState(false);
  return children({ on, toggle: () => setOn(v => !v) });
}

<Toggle>
  {({ on, toggle }) => (
    <button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>
  )}
</Toggle>

Render Props vs Custom Hooks

Custom hooks replaced most render prop use cases. Compare:

// Render prop
<MouseTracker render={pos => <Cursor {...pos} />} />

// Custom hook — simpler, no extra nesting
function CursorComponent() {
  const pos = useMousePosition();
  return <Cursor {...pos} />;
}

Prefer hooks for new code. Use render props when:

  • You need the parent to control the rendering context (e.g., inside a specific DOM element)
  • Building a headless component library where consumers need full render control

Performance Caveat

Inline render functions are new references on every parent render. If the receiving component uses React.memo, it still re-renders because the prop changed. Memoize with useCallback or restructure to avoid.


Canvas is not supported in your browser