React Render Props Pattern

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.

  • MouseTracker keeps the mouse position in state and updates it on move.
  • It takes a render prop, 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.

  • App passes a function as the render prop.
  • That function receives pos from MouseTracker and returns JSX showing it.
  • MouseTracker handles tracking; App handles display.
  • Another component could pass a different render to show the position as a moving dot instead.

Output

(move the mouse inside the box)
Mouse is at 120, 75
Mouse 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 children is a function, and the component calls children(pos).
  • The caller writes the function directly between the opening and closing tags.
  • It’s the same idea as render, just using the children prop, 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 work
return <div>{render}</div>;
// ✅ call it with the data
return <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 use children as 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 children as the function instead of a render prop
  • ✅ 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. 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. 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. 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. 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.

React Code Splitting

Share & Connect