React Render Props
As React applications grow, multiple components often need to share common logic — like fetching data, handling mouse movements, or managing state.
Instead of duplicating that logic, React provides a pattern called Render Props, which allows you to share functionality between components in a clean and reusable way.
What is a Render Prop?
A Render Prop is a technique for sharing code between React components using a function that returns JSX.
It’s called a render prop because a prop is used to tell a component what to render.
Example:
- Let’s say you want to track mouse movements on the screen.
- Without render props, you might repeat the same logic in different components.
- With render props, you can share it easily.
import React, { useState } from "react";
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
function handleMouseMove(event) {
setPosition({
x: event.clientX,
y: event.clientY,
});
}
return (
<div style={{ height: "200px" }} onMouseMove={handleMouseMove}>
{render(position)}
</div>
);
}
function App() {
return (
<MouseTrackerrender={({ x, y }) => (
<h3>
Mouse position: ({x}, {y})
</h3>
)}
/>
);
}
export default App;
- The
MouseTrackercomponent handles the logic for tracking mouse movement. - Instead of deciding what to render itself, it calls the
render()prop. - The parent (
App) defines how the data is displayed.
This keeps the logic reusable while allowing flexible UI rendering.
Why Use Render Props?
Render Props are great when you want to:
- Share logic between components
- Avoid duplicating stateful logic
- Allow flexible UI composition
- Separate logic (what happens) from presentation (how it looks)
Example 2: Sharing Logic for Data Fetching
Here’s how you can use render props to share data fetching functionality:
import React, { useEffect, useState } from "react";
function DataFetcher({ url, render }) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((result) => {
setData(result);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
function App() {
return (
<div>
<h2>Render Props Example – Posts</h2>
<DataFetcherurl="<https://jsonplaceholder.typicode.com/posts>"
render={({ data, loading }) =>
loading ? (
<p>Loading...</p>
) : (
<ul>
{data.slice(0, 5).map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
/>
</div>
);
}
export default App;
How it works:
- The
DataFetchercomponent contains the reusable data-fetching logic. - It doesn’t render UI by itself — it delegates rendering to whatever is passed in the
renderprop. - The
Appdecides how the data should look.
Alternative Syntax – Using Children as a Function
Instead of using a render prop, you can use the children prop as a function.
This is another common pattern in React.
function MouseTracker({ children }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
function handleMouseMove(e) {
setPos({ x: e.clientX, y: e.clientY });
}
return (
<div onMouseMove={handleMouseMove} style={{ height: "200px" }}>
{children(pos)}
</div>
);
}
function App() {
return (
<MouseTracker>
{({ x, y }) => (
<p>
Mouse coordinates: <strong>{x}, {y}</strong>
</p>
)}
</MouseTracker>
);
}
Render Props vs Higher-Order Components (HOC)
| Feature | Render Props | Higher-Order Components (HOC) |
|---|---|---|
| Definition | Uses a function prop to render content | Wraps a component in another function |
| Flexibility | Very flexible and explicit | May lead to nested components |
| Code Reuse | Great for state sharing and logic reuse | Great for enhancing components |
| Modern Use | Often replaced by custom hooks | Often replaced by hooks too |
When to Use Render Props
Use Render Props when you need:
- To share logic between multiple components.
- To customize UI rendering while reusing the same functionality.
- For state management patterns like mouse tracking, form handling, or animations.
