Back to all notes

Render Props Pattern: Pass a Renderer via a Named Prop

Similar to function children, but explicit—pass a render function to customize output.

2 min read

Introduction

The render props pattern passes a function via a named prop (like renderItem) so consumers can control how data is rendered while sharing list or layout logic.

Why this matters

Function children are powerful but not always self‑documenting. Render props make the API explicit: you pass a renderItem (or similar) function to control how items are displayed.

The problem

We want a reusable List component that delegates how each item looks, without reimplementing list logic everywhere.

Inefficient approach

Mapping inline in every place you need a list creates duplication:

function Fruits() {
  const items = ["Apple", "Banana", "Cherry"];
  return (
    <ul>
      {items.map((item, index) => <li key={index}>{item}</li>)}
    </ul>
  );
}

function App() {
  return (
    <div>
      <h1>Render Props Pattern</h1>
      <Fruits />
    </div>
  );
}

export default App;

The solution

Accept a renderItem prop and call it for each item.

function List({ items, renderItem }) {
  return (
    <ul>
      {items.map((item, index) => renderItem(item, index))}
    </ul>
  );
}

function App() {
  const items = ["Apple", "Banana", "Cherry"];
  return (
    <div>
      <h1>Render Props Pattern</h1>
      <h2>List</h2>
      <List items={items} renderItem={(item, index) => <li key={index}>{item}</li>} />
    </div>
  );
}

export default App;

Step-by-step

  1. Create a reusable List that takes items and renderItem.
  2. Inside List, map over items and call renderItem(item, index).
  3. In the parent, pass a function that returns a keyed <li>.
  4. Keep all item rendering logic outside of List.
  5. Reuse List with different renderItem implementations as needed.

Tips

  • Name the render prop by intent (renderRow, renderEmpty, renderHeader).
  • Keep the render function pure; avoid side effects inside it.
  • If you pass several render functions, consider a Compound Components pattern instead.

Knowledge base

Problem snippet:

{items.map((item, index) => <li key={index}>{item}</li>)}

Solution snippet:

<List items={items} renderItem={(item, i) => <li key={i}>{item}</li>} />