Render Props Pattern
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
<!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
<!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
<!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>
- 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.
- Creating a new inline function on every render for the render prop, which can cause unnecessary re-renders in optimized child components.
- Overusing render props today when a custom hook would be simpler — hooks have replaced most historical use cases for this pattern.
- 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.
Works in any React version supporting function components and props (React 0.14+); most commonly seen in pre-Hooks (pre-16.8) codebases.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: