Skip to main content

Render Props

Render props share stateful logic by passing a function the component calls with its internal state, letting callers control rendering. It is explicit but prone to nesting, and custom hooks now cover most use cases.

Type
Behavioral
When to Use
Share Stateful Logic, Invert Rendering Control, Avoid Hoc Nesting, Reuse Behavior

The render props pattern shares behavior between components by passing a function as a prop. A component owns some state or logic and, instead of rendering fixed markup, calls the provided function with that state so the caller decides what to display. The name comes from the conventional render prop, though any function prop — often children — works.

How It Works

A provider component manages internal state and invokes the function prop in its render output: return this.props.render(state). The consumer passes a function that maps the exposed state to UI. A classic example is a <MouseTracker> that tracks cursor position and calls children({ x, y }), letting different consumers render a tooltip, a crosshair, or coordinates from the same tracking logic.

This inverts control: the reusable component handles the how (state, subscriptions, cleanup) while the consumer supplies the what (markup). Libraries such as React-Router (<Route render={...}>) and older form and data-fetching libraries popularized the technique.

When to Use It

Use render props to reuse stateful, behavioral logic while keeping rendering fully in the caller's hands — useful for trackers, toggles, data loaders, and virtualization where consumers need varied output. In class-era React it was the flexible alternative to HOCs because the data flow is explicit: you can see exactly what arguments the function receives.

Trade-offs

Heavy use leads to deeply nested callbacks in JSX ("callback pyramids") that hurt readability. Defining the function inline creates a new closure on every render, which can defeat memoization unless handled carefully. As with HOCs, custom hooks now cover most cases more cleanly: a hook returns the state directly with no extra nesting. Render props persist where a component genuinely needs to control a child's render slot or interleave its own markup.

Related Patterns

Higher-order components solve the same logic-sharing problem by wrapping rather than by callback. Custom hooks are the modern successor to both. Compound components offer another way to share implicit state with children, and the container/presentational split addresses the same separation of data from rendering.

Example

class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };
  handleMove = e => this.setState({ x: e.clientX, y: e.clientY });
  render() {
    return (
      <div onMouseMove={this.handleMove}>
        {this.props.children(this.state)}
      </div>
    );
  }
}

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

The tracking logic lives once in MouseTracker; each consumer chooses its own rendering. A hooks rewrite would expose a useMouse() returning { x, y } with no wrapping element.