5 questions foundWhat is the render props pattern in React?
Intermediate The render props pattern means passing a function as a prop to a component, and that component calls the function to decide what to actually display, often passing some internal data or state into that function. This lets you share logic between components while letting the parent fully control what gets rendered.
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
function handleMouseMove(e) {
setPosition({ x: e.clientX, y: e.clientY });
}
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
<MouseTracker render={(pos) => <p>Mouse is at {pos.x}, {pos.y}</p>} />
Real-world example A shared MouseTracker component tracks the mouse position once, and different pages use it to display that position in completely different ways, like showing coordinates in one place and moving an image in another, simply by passing a different render function.
Common follow-ups: How is the render props pattern different from simply passing children as a function?;Why has this pattern become less common since custom hooks were introduced?
Custom Hooks;Higher-Order Components (HOCs)
How would you rewrite a render props component as a custom hook instead?
Advanced You move the shared logic, like tracking state or subscribing to something, directly inside a custom hook, and return whatever data the component needs. The component using the hook then decides how to render that data directly in its own JSX, without needing an extra wrapping component or a function prop at all.
// Render props version
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>;
}
// Custom hook version, simpler and flatter
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMove(e) { setPosition({ x: e.clientX, y: e.clientY }); }
window.addEventListener('mousemove', handleMove);
return () => window.removeEventListener('mousemove', handleMove);
}, []);
return position;
}
Real-world example A team refactors an older MouseTracker render props component into a simple useMousePosition custom hook, removing an unnecessary wrapping component and making the code easier for new developers to follow.
Common follow-ups: Are there still cases today where render props are a better choice than a custom hook?;What extra flexibility does the render props pattern offer that a hook alone cannot?
Custom Hooks;Higher-Order Components (HOCs)
What is a common naming alternative to render props, using children as a function instead?
Intermediate Instead of a named prop called render, some components accept children as a function directly, which reads a bit more naturally in JSX since it looks like normal nested content, even though it is technically still a function being called by the parent component.
function DataProvider({ children }) {
const [data, setData] = useState(null);
useEffect(() => { fetchData().then(setData); }, []);
return children(data);
}
<DataProvider>
{(data) => (data ? <p>{data.name}</p> : <p>Loading</p>)}
</DataProvider>
Real-world example A shared DataProvider component uses children as a function so that using it in JSX still looks like normal nested content, even though it is actually calling a function to decide exactly what gets displayed.
Common follow-ups: Is there a real functional difference between using a prop called render versus using children as a function?;Which style do most modern component libraries prefer today?
Component Composition & Children Props;Custom Hooks
What is a simple real world example of the render props pattern being useful?
Beginner A component that tracks whether the browser window is online or offline could use render props, letting different parts of an app show that information in completely different ways, such as a small icon in one place and a full banner message in another, all powered by the exact same shared logic.
function OnlineStatus({ render }) {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setIsOnline(true);
const goOffline = () => setIsOnline(false);
window.addEventListener('online', goOnline);
window.addEventListener('offline', goOffline);
return () => {
window.removeEventListener('online', goOnline);
window.removeEventListener('offline', goOffline);
};
}, []);
return render(isOnline);
}
Real-world example A messaging app uses a shared OnlineStatus component to show a small green dot in the header and a full warning banner at the top of the chat screen, both powered by the exact same online tracking logic through render props.
Common follow-ups: Would this same online status logic be simpler to share using a custom hook instead?;How would you test a component that uses the render props pattern?
Custom Hooks;Testing
What is a downside of the render props pattern that developers should be aware of?
Intermediate Nesting several render props components inside each other can create deeply indented, harder to read code, sometimes called callback nesting, similar to the wrapper hell problem seen with higher order components. This is one of the main reasons custom hooks became the preferred way to share logic once they were introduced.
// Deeply nested and harder to read
<MouseTracker render={(mouse) => (
<WindowSize render={(size) => (
<ScrollPosition render={(scroll) => (
<Display mouse={mouse} size={size} scroll={scroll} />
)} />
)} />
)} />
Real-world example A team dealing with three levels of nested render props components decides to refactor all three into separate custom hooks instead, flattening the code and making it much easier to read and maintain.
Common follow-ups: How many levels of nesting is generally considered too many before refactoring?;How would combining these three examples into custom hooks actually look in code?
Custom Hooks;Higher-Order Components (HOCs)