← Back to React Course | Chapter 6: Custom Hooks & Advanced Patterns | Lesson 7 of 10

Render Props Pattern

The render props pattern is like handing a chef a plate and letting them decide what to put on it, while you take care of gathering the ingredients.

What a Render Prop Is

A render prop is a prop whose value is a function that returns JSX. Instead of a component deciding what to display internally, it calls this function and renders whatever it returns. This lets the component supply data or behavior while the caller controls the exact markup.

Note: The function doesn't have to be named render — children is often used as a render prop too, called as a function instead of rendered as JSX directly.

Warning: Don't confuse a render prop (a function that returns JSX) with a regular prop that just happens to hold a React element — they're used very differently.

Example: What a Render Prop Is

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function MouseTracker({ render }) {
  const [pos, setPos] = React.useState({ x: 0, y: 0 });
  return (
    <div onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })} style={{height: 100, border: "1px solid #333"}}>
      {render(pos)}
    </div>
  );
}
function App() {
  return <MouseTracker render={pos => <p>Mouse at {pos.x}, {pos.y}</p>} />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Using 'children' as a Render Prop

Instead of a custom-named prop, you can use the built-in children prop as a function. React doesn't require children to be JSX — it can be any value, including a function, which the component then calls and renders wherever it wants.

Note: This style reads nicely at the call site since it looks like normal JSX nesting, just with a function inside instead of elements.

Warning: Because children as a function looks unusual to newcomers, add a short comment explaining the pattern if you use it in shared/library code.

Example: Using 'children' as a Render Prop

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function Toggle({ children }) {
  const [on, setOn] = React.useState(false);
  return children({ on, toggle: () => setOn(o => !o) });
}
function App() {
  return (
    <Toggle>
      {({ on, toggle }) => (
        <button onClick={toggle}>{on ? "Visible" : "Hidden"}</button>
      )}
    </Toggle>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Render Props vs. Custom Hooks

Everything a render-props component can do, a custom hook can usually do more simply, without adding an extra layer of JSX nesting. The MouseTracker example above can be rewritten as a useMousePosition hook that any component calls directly, no wrapping component required.

Note: For new code, reach for a custom hook first — use render props mainly when working with an existing library that already expects that pattern.

Warning: Deeply nested render props (a render prop inside a render prop inside another) creates hard-to-read 'wrapper hell', one of the exact problems hooks were introduced to solve.

Example: Render Props vs. Custom Hooks

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
function useMousePosition() {
  const [pos, setPos] = React.useState({ x: 0, y: 0 });
  const handleMove = e => setPos({ x: e.clientX, y: e.clientY });
  return [pos, handleMove];
}
function App() {
  const [pos, handleMove] = useMousePosition();
  return (
    <div onMouseMove={handleMove} style={{height: 100, border: "1px solid #333"}}>
      <p>Mouse at {pos.x}, {pos.y} (hook version, no wrapper needed)</p>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
Common Mistakes
  1. Forgetting that the prop doesn't have to be literally called render — any prop that's a function returning JSX counts as a render prop.
  2. Creating a new inline function on every render for the render prop, which can cause unnecessary re-renders in optimized child components.
  3. Overusing render props today when a custom hook would be simpler — hooks have replaced most historical use cases for this pattern.
Chapter Summary
  • The render-props pattern is a component that takes a function as a prop and calls it to determine what to render.
  • It lets a component share stateful logic while letting the caller fully control the resulting UI.
  • It predates hooks and was the primary way to share logic between components before React 16.8.
  • Custom hooks now cover most of what render props used to be needed for, but the pattern still appears in some libraries.
Browser Support

Works in any React version supporting function components and props (React 0.14+); most commonly seen in pre-Hooks (pre-16.8) codebases.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.