DesignDev.io logoDesignDev.io logo

  1. Home
  2. /React Foundations
  3. /The Render Props Pattern Is Not Dead — Here's Where It Still Wins
← Back to home

The Render Props Pattern Is Not Dead — Here's Where It Still Wins

Alex Chen

Alex Chen

Cover Image for The Render Props Pattern Is Not Dead — Here's Where It Still Wins
Alex Chen

Alex Chen

January 20, 2026·4 min read
Updated April 26, 2026
Series:React Foundations
Level:Intermediate
Tags:reactweb
Assumes:Comfortable with React hooks, compound components, and TypeScript generics

Hooks replaced most render props use cases in 2019. Most — not all. There are three scenarios where render props still outperform hooks and compound components. Here's what they are and how to implement them cleanly.


When React hooks shipped in 2019, the community declared render props dead. The pattern that had powered libraries like Formik, React Motion, and Downshift was suddenly unnecessary. You could extract any stateful logic into a hook and call it directly in your component. Cleaner. Simpler. No wrapper components.

That conclusion was mostly right. For most of what render props were doing — sharing stateful logic — hooks are better.

But "mostly right" is doing work in that sentence. There are cases where hooks can't replace render props, and forcing hooks into those cases produces code that's more complex, not less.

The question worth asking is not "render props or hooks?" It's "what problem am I actually solving?" The answer determines the tool.


What render props actually are

A render prop is a prop whose value is a function that returns JSX. The component calls that function and renders whatever it returns.

The component owns the logic. The consumer owns the rendering. That separation is the pattern's essential feature — and the one that hooks can't always replicate.


Why hooks replaced most render props

Before hooks, sharing stateful logic required either higher-order components or render props. Both introduced wrapper components. Deep component trees. The infamous "wrapper hell" that made React DevTools debugging painful.

Hooks collapsed this entirely:

For sharing logic, hooks win. No wrapper component. No nesting. The state lives wherever you call the hook.


Where render props still win

Scenario 1: the consumer controls rendering structure

This is the case hooks genuinely can't handle. When a component needs to provide logic but the consumer needs to control not just what renders, but where and how it renders in the DOM structure.

A virtual list is the clearest example. The list component handles the complex work — measuring item heights, calculating visible range, managing scroll position. But each row can be anything — a card, a table row, a grid cell, a custom layout. The list doesn't know and shouldn't know.

The consumer renders whatever they need inside each row — and gets the positioning styles passed in so they don't have to calculate anything:

Try implementing this with hooks. A useVirtualList hook would return the visible items and the scroll handler — but it can't tell the consumer how to position each item in its specific DOM structure. The render prop passes the style as an argument, letting the consumer apply it wherever makes sense for their layout.


Scenario 2: injecting logic into a specific DOM position

Sometimes you need a component to render something at an exact position in the consumer's JSX — not as a sibling, not wrapped, but injected into the middle of existing markup.

A DataTable with a render prop for custom cell content:

The cell render prop lives inside a <td> that the DataTable owns. There's no way to express "render this inside the cell for this column" with a hook — hooks don't return DOM structure, they return values.

Usage:

Three different cell types — a badge, a formatted number, a button — all living in the right DOM position because the render prop puts them there. A hook couldn't do this without the consumer building the entire table structure themselves.


Scenario 3: the hybrid pattern

The most useful evolution of render props is the hybrid: a hook for the logic, a render prop for the parts that need custom layout.

Usage with two completely different item layouts — same logic, different rendering:

Same keyboard navigation, same filtering, same state management — two completely different visual presentations. The hook owns all the logic. The render prop owns all the rendering. Neither compromises on its responsibility.

This hybrid is the state of the art for UI libraries like Downshift and Headless UI. It's why they're so flexible — the logic is complete and the rendering is entirely open.


The API design question

When building a component that might use render props, one naming decision matters: render prop vs children as a function.

Both work. children as a function reads more naturally for content-level rendering — the thing inside the component is its content. A named prop like renderItem or renderCell reads more naturally for slot-level customisation — you're customising a specific part, not the whole content.

The convention in modern libraries: children as a function for the primary content, named render props for specific slots.


The honest summary

Render props are not the default. Hooks are simpler and should be your first choice for sharing logic.

Reach for render props when:

  • The consumer needs to control rendering structure at a specific DOM position
  • A virtualised or windowed list needs to render arbitrary item shapes
  • You want a hybrid: a hook for logic + a render prop for flexible rendering

In those cases, the render prop isn't a workaround — it's the right tool.


Up next: How to manage global state without a library — the useSyncExternalStore pattern for when you don't want React Context or Zustand.

Related: Compound components — the design pattern that changed how I build UIs — the context-powered pattern that often pairs with render props for maximum flexibility.

Alex Chen is a senior frontend engineer who writes about React patterns, JavaScript internals, and the decisions that separate maintainable codebases from ones that fight back. Opinionated by design.

Photo by Alexander Travkin on Unsplash


More Stuff

Building a Badge Component — Variants, Sizes, and Dot Indicators
April 3, 2026·3 min read

Building a Badge Component — Variants, Sizes, and Dot Indicators

Badge is the first purely presentational primitive in this series — no form integration, no event handling, no focus management. That simplicity is deceptive. A Badge that looks right in isolation often breaks inside a nav item, a table cell, or an avatar. The decisions that make it composable are worth getting right now.

Muhammad Athar

Muhammad Athar

Building a Label Component That Actually Connects to Its Field
April 3, 2026·3 min read

Building a Label Component That Actually Connects to Its Field

We've referenced a Label component in every form field article so far without building it. That's intentional — you need to understand what the Label connects to before you understand what it needs to do. Now that we have Input, Textarea, Checkbox, RadioGroup, Select, and Toggle, the Label's job is clear: it's not just styled text. It's the accessible name of a form field, and it carries required indicators, optional markers, and disabled state as part of that responsibility.

Muhammad Athar

Muhammad Athar

DesignDev.io logoDesignDev.io logo

Explore

  • Home
  • Search
  • Authors
  • Series
  • About Us

Account

  • Sign in

Legal

  • Privacy Policy
  • Terms of Service

© 2026 DesignDev.io · All rights reserved

Built with Next.js · Tailwind · Sanity · Vercel