← Back to Chapters

Render Props Pattern

? Render Props Pattern

⚛️ Introduction

The Render Props pattern in React is a technique for sharing logic between components using a function as a prop.

It allows components to delegate rendering control to their children, enabling highly reusable and customizable logic.

? What Is a Render Prop?

A Render Prop is simply a prop that takes a function as its value. The function returns JSX that defines how the component should render.

? View Code Example
// Basic render prop structure
<DataProvider render={(data) => <Display data={data} />} />

Here, the DataProvider component provides logic, and Display handles how to render it.

? Basic Example: Mouse Tracker

? View Code Example
// MouseTracker component using render props
import React, { useState } from "react";

function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });

const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};

return (
<div style={{ height: "150px", border: "2px dashed gray" }} onMouseMove={handleMouseMove}>
{render(position)}
</div>
);
}

export default MouseTracker;
? View App Usage
// Parent controls rendering
import MouseTracker from "./MouseTracker";

function App() {
return (
<div>
<h3>Render Props Example</h3>
<MouseTracker render={({ x, y }) => (
<p>Mouse Position: X={x}, Y={y}</p>
)} />
</div>
);
}

The MouseTracker manages the mouse logic, while the parent decides how to display it.

? Advantages of Render Props

  • Promotes reusability of logic without duplicating code.
  • Separates logic from presentation.
  • Gives parent components full control over rendering.
  • Works with both class and functional components.

? Example: Data Fetching Component

? View Code Example
// DataFetcher exposes loading and data state
import React, { useEffect, useState } from "react";

function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch(url)
.then(res => res.json())
.then(json => {
setData(json);
setLoading(false);
});
}, [url]);

return render({ data, loading });
}

export default DataFetcher;
? View App Usage
// Parent decides UI for loading and data
<DataFetcher
url="https://jsonplaceholder.typicode.com/users"
render={({ data, loading }) =>
loading ? (
<p>Loading...</p>
) : (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
/>

The parent decides how to display loading or data states — the DataFetcher only provides the logic.

⚙️ Comparison: HOC vs Render Props

Aspect HOC Render Props
Definition Function that wraps a component Prop expecting a function
Code Structure External wrapping Inline JSX
Usage Cross-cutting logic Flexible rendering
Modern Replacement Custom Hooks Custom Hooks

⚠️ Common Pitfalls

  • Deep nesting can cause callback hell.
  • Reduced readability with many render props.
  • Often replaced by custom hooks.

? Tips

  • Use clear and descriptive render prop names.
  • Prefer hooks for simpler reuse.
  • Keep render functions focused.

? Try This

  1. Create a WindowSize render-prop component.
  2. Expose width and height.
  3. Render it in different formats.
  4. Compare with a custom hook.

Goal: Understand flexible logic reuse with render functions.