React Render Props Pattern
Table of Contents + −
In the last lesson, React Higher Order Components, you reused logic by wrapping a component. This lesson covers render props, a sibling pattern that shares the same logic by calling a function you give it instead of wrapping.
🤔 Why do render props exist?
It’s the same problem as HOCs, sharing logic between components, just with a different twist on how.
- A component knows something useful, like the mouse position or whether data is loading.
- Many different components want that knowledge, each displaying it their own way.
- You want to share the logic but let each caller decide the display.
- HOCs do this by wrapping. Render props do it by handing the data to a function the caller provides.
So render props is about sharing logic while letting the caller stay fully in control of what gets rendered.
🐍 What is a render prop?
Let’s define it plainly.
- A render prop is a prop whose value is a function that returns JSX.
- The component does its work, then calls that function, passing its data in.
- The caller’s function decides what to render with that data.
Think of it like ordering a custom-made item. You hand over the design (the function), the workshop does the work and uses your design to produce the result.
🧩 Building a component with a render prop
Let’s build a MouseTracker that knows the mouse position and shares it through a render prop. The component tracks; the caller renders.
import { useState } from "react";
function MouseTracker({ render }) { const [pos, setPos] = useState({ x: 0, y: 0 });
function handleMove(e) { setPos({ x: e.clientX, y: e.clientY }); }
return ( <div onMouseMove={handleMove} style={{ height: "200px" }}> {render(pos)} {/* call the function, passing the data */} </div> );}Read what the component does.
MouseTrackerkeeps the mouse position in state and updates it on move.- It takes a
renderprop, which is a function. - Instead of rendering fixed UI, it calls
render(pos), handing over the current position. - Whatever that function returns is what shows on screen.
So MouseTracker owns the logic, the position, but hands the rendering decision to the caller via render(pos).
🖥️ Using the render prop
Now the caller decides what to do with the position. Two different callers can render it completely differently, reusing the same logic.
function App() { return ( <MouseTracker render={(pos) => ( <p> Mouse is at {pos.x}, {pos.y} </p> )} /> );}See the split of responsibilities.
Apppasses a function as therenderprop.- That function receives
posfromMouseTrackerand returns JSX showing it. MouseTrackerhandles tracking;Apphandles display.- Another component could pass a different
renderto show the position as a moving dot instead.
Output
(move the mouse inside the box)Mouse is at 120, 75Mouse is at 145, 82👶 The children-as-a-function variation
A very common version uses children as the function instead of a prop named render. It reads a bit more naturally.
function MouseTracker({ children }) { const [pos, setPos] = useState({ x: 0, y: 0 }); // ... same handleMove ... return <div onMouseMove={handleMove}>{children(pos)}</div>;}
// used with a function between the tags<MouseTracker> {(pos) => <p>Mouse at {pos.x}, {pos.y}</p>}</MouseTracker>Quick note on this variation.
- Here
childrenis a function, and the component callschildren(pos). - The caller writes the function directly between the opening and closing tags.
- It’s the same idea as
render, just using thechildrenprop, which many find cleaner.
So whether it’s a render prop or children as a function, the pattern is identical: the component calls your function with its data.
🆚 Render props vs hooks
The honest part again: custom hooks have largely replaced render props for sharing logic.
- A custom hook like
useMousePosition()shares the same logic with no extra component or nesting. - Render props can lead to deeply nested functions when you combine several, sometimes called “the pyramid”.
- Hooks are flatter and easier to read for the same job.
- So for new code, a custom hook is usually the better choice.
So, like HOCs, learn render props mainly to recognize them in existing code and libraries. For your own new logic-sharing, reach for a custom hook.
Hooks are usually cleaner now
Render props solved a real problem before hooks existed. Today a custom hook does the same job with less nesting and clearer code. Know render props to read older code; prefer hooks when writing new code.
⚠️ Common Mistakes
A common mistake is rendering the function prop as if it were JSX, instead of calling it.
// ❌ render is a function, not JSX - this won't workreturn <div>{render}</div>;
// ✅ call it with the datareturn <div>{render(pos)}</div>;Keep these in mind.
- Don’t forget to call the function prop, like
render(pos), passing the data. - Don’t confuse render props with regular children; here the child is a function you call.
- Don’t reach for render props in new code when a custom hook is simpler.
✅ Best Practices
A few habits for render props.
- Use a clear name like
render, or usechildrenas a function, whichever reads better. - Call the function with the data the caller needs, like
render(pos). - Keep the logic in the component and the display decision in the caller’s function.
- For new logic-sharing, prefer a custom hook; use render props to understand existing code.
Same goal, three patterns
HOCs, render props, and custom hooks all solve the same problem: reusing logic across components. They’re three eras of the same idea. Hooks are the modern answer, but knowing all three makes any React codebase readable.
🧩 What You’ve Learned
- ✅ A render prop is a prop whose value is a function returning JSX
- ✅ The component does the logic, then calls that function with its data, like
render(pos) - ✅ The caller’s function decides what to render, so logic and display are separate
- ✅ A common variation uses
childrenas the function instead of arenderprop - ✅ Remember to call the function prop, not render it as JSX
- ✅ Custom hooks have largely replaced render props for new code, but you’ll still see them around
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is a render prop?
Why: A render prop is a function passed as a prop. The component does its work and calls that function with its data, letting the caller decide what to render.
- 2
Who decides what gets rendered with a render prop?
Why: The component supplies the data by calling the function, but the caller's function decides the actual JSX, so display stays with the caller.
- 3
What is the children-as-a-function variation?
Why: Instead of a prop named render, the function is passed as children between the tags, and the component calls children(pos). It's the same pattern, cleaner syntax.
- 4
What has largely replaced render props in new code?
Why: Custom hooks share logic with less nesting and clearer code. Render props are still worth recognizing in existing code, but hooks are the modern choice.
🚀 What’s Next?
Now you know the three logic-sharing patterns: HOCs, render props, and hooks. Next we switch from sharing logic to loading code smartly. You’ll learn code splitting, which loads parts of your app only when they’re needed.