Overview
What Is a Render Prop?
The Goal: A Flexible List Component
Without Render Props:
Without Render Props
function List({ items }) {
return (
<ul>
{items.map(item => (
<li>{item.label}</li>
))}
</ul>
);
}
- Add an icon
- Wrap items in a link
- Style them differently
With Render Props: Clean Separation
List.tsx
List Component with Render Props
type ListProps<T> = {
items: T[];
children: (item: T, index: number) => React.ReactNode;
};
{/*
To enforce that each item has an id:
type ListProps<T extends { id: string }> = {
items: T[];
children: (item: T, index: number) => React.ReactNode;
};
*/}
function List<T>({ items, children }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{children(item, index)}</li>
))}
</ul>
);
}
- T is a generic type — this list can handle any data structure.
- children is a function that receives each item and index.
Usage Example
Basic Usage Example
const users = [
{ id: 1, name: 'Alice', avatar: '👩' },
{ id: 2, name: 'Bob', avatar: '👨' },
];
<List items={users}>
{(user) => (
<div>
<span>{user.avatar}</span> {user.name}
</div>
)}
</List>
Wrapping Items in Links
<List items={users}>
{(user) => (
<a href={`/profile/${user.id}`}>
{user.name}
</a>
)}
</List>
Benefits of Render Props
- ✅ Highly flexible — rendering is in your control.
- 🔁 Logic reuse — the logic to loop, wrap in <ul>, handle keys stays inside.
- 🧠 Generic-friendly — works perfectly with TypeScript generics.
Render Props vs Hooks
- Encapsulated logic
- Render control
Real-World Inspiration
- 🔗 React Router: <Route render={...} />
- 🔍 Hero UI |(Next UI)
- 📅 React Table: Render Props for rows and cells (older versions)
Gotchas
- 🌀 Avoid deeply nested render props (callback hell).
Summary
- Build logic-heavy but UI-agnostic components.
- Delegate rendering to the user.
- Stay type-safe and generic with TypeScript.