VOOZH about

URL: https://blog.logrocket.com/react-reference-guide-render-props/

⇱ React Reference Guide: Render props - LogRocket Blog


2020-09-04
1421
#react#reference guide
Ovie Okeh
24391
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

A render prop is simply a function prop that is called in a render method. To truly understand what this means, let’s learn why such a pattern exists in the first place, then explore the various implementations.

👁 LogRocket React Reference Guide: Render props

Jump ahead:


React has made building interactive applications a lot more manageable. We’re able to break up our app into components, each with their own encapsulated logic and styling. It’s great — it means we’re able to work on lots of smaller problems instead of one really large one.

This method of building applications works really well because of abstractions. A component in an application is just an abstraction of logic, data, and, in some cases, styling.

const Button = () => (...)

The Button component above is an abstraction of the logic and styles required to make a button show up on a page. Now, every time we want a button, we simply use this abstraction — no need to reinvent the wheel.

When we build apps this way, a new challenge arises: sometimes we want to share logic or data between components. We also want to do this in a way that is clean and composable. Just as we’re able to abstract logic and data into components, we also want to be able to compose these components to form larger building blocks.

Four paragraphs in and we still haven’t gotten to render props yet.

Put simply, a render prop is a function prop that is called in a render method. But to truly understand what this means, we need to know why such a pattern exists in the first place.


🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Why do we need render props?

Let’s take a legitimate problem we might face in the real world. Say we wanted to have a UI element that we could dismiss with an animation.

👁 Delete from table UI
Delete from table by Alex Lauderdale.

What we want here is a component that can render anything, but we want to keep this dismissal animation. We could simply use the children prop and pass whatever we wanted to render as a child.

const Dismiss = ({ children }) => {
 return (
 <Fragment>
 {children}
 </Fragment>
 )
}

That would work, but it’s limited.

  • How would we fire the dismiss event from within the component being rendered? If we decide to fire it from the Dismiss component, that means we’ll have to hardcode some HTML.
  • What if we wanted to set up different actions to fire on the dismiss event?
  • Is there any way to pass data as props to the children being rendered?

In situations like this, we need something a bit more flexible than the children prop method. What we really want is to encapsulate the dismissal functionality so that we can share it between components.

With render props, we could render anything we wanted, just like the children prop method. But we will also be able to pass props to whatever is being rendered.


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

This is the main purpose of render props — being able to communicate with any rendered component without having to couple implementation details. This is possible because the render function now comes in as a prop, and you can pass data to it as arguments when you call it.

This will become clearer when we take a look at a practical use case for render props (based on WAVE’s modal component). We’ll look at a possible solution to the problem we raised initially using the children prop method. Then, we’ll look at a more flexible solution with render props.

Now that you know the purpose of props, see how devs use them for modern reusable components.

Implementing render props

const Dismiss = ({ children }) => {
 const dismiss = () => {
 ...code to implement dismissal animations etc
 }

 return (
 <div>
 {children}
 <button onClick={dismiss}>dismiss</button>
 </div>
 )
}

const DismissableContent = () => {
 return (
 <Dismiss>
 ...code
 </Dismiss>
 )
}

Above, we have a Dismiss component that renders whatever is passed as the children prop but also contains code to implement the dismiss animation. Below it is how we’d use such a component. Notice that with this implementation:

  • You can only fire the dismiss event from within the Dismiss component
  • You can’t pass data to the children being rendered
  • You have to account for the extra div in your layouts

You can see some of the limitations in sharing functionality this way. Now let’s look at the same solution, but with render props:

const Dismiss = (props) => {
 const dismiss = () => {
 ...code to implement dismissal animations etc
 }

 return props.render(dismiss)
}

const DismissableContent = () => {
 return (
 <Dismiss render={
 dismiss => <Content dismiss={dismiss} />
 } />
 )
}

Apart from the code being smaller, there are some more important advantages to this method.

We have moved the content of the render function from being hardcoded in the Dismiss component to accepting it from the props. By simply calling props.render(), the component is able to render whatever is returned from the render prop. This comes with some implications:

  • Since the render prop is a function, we can pass data to it as arguments. This means that Dismiss can communicate with the component being rendered and vice versa
  • You don’t have to account for any extra divs from Dismiss because it can be pure and not return its own HTML (most times, though, you’d return some accompanying HTML and styling)

We can now see how this solves the issues we had using the children prop method. This is much cleaner and more flexible by every metric.


Implementing other props

Another advantage of render props is that the prop doesn’t have to be called render. You could call it whatever you want as long as the prop is invoked in the render method. This leads to interesting implementations, like this:

const Dismiss = ({ children }) => {
 const dismiss = () => {
 ...code to implement dismissal animations etc
 }

 return children(dismiss)
}

const DismissableContent = () => {
 return (
 <Dismiss>
 {(dismiss) => (
 <Content dismiss={dismiss} />
 )}
 </Dismiss>
 )
}
Move on to mastering the basics of props with this comprehensive tutorial.

Implementing higher-order components (HOCs)

You may have noticed by now that render props also allow us to implement HOCs with minimal effort. Below, you can see a withDismiss HOC that takes in a component and enhances a component with the dismiss functionality.



const withDismiss = (Component) =>
 (props) => (
 <Dismiss>
 {(dismiss) => (
 <Component dismiss={dismiss} {...props} />
 )}
 </Dismiss>
 )

This is so easy because of the compositional nature of components in React. Because we’ve been able to encapsulate the dismiss logic into a component with render props functionality, composing new components with the same functionality becomes effortless.

Dive deeper into HOCs with a more advanced use case: fetching data.

Caveats

There’s an important edge case to keep in mind when using the render props pattern if you use React.PureComponent. For a quick refresher, PureComponent can provide small performance gains because it implements shouldComponentUpdate internally through a shallow props and state comparison.

The important thing to note here is the shallow props comparison. Most of the time, when you use render props, you’ll be passing an anonymous function to the render prop.

class Dismiss extends React.PureComponent {
 dismiss = () => {
 ...code to implement dismissal animations etc
 }

 render() {
 return this.props.render(dismiss)
 }
}

const DismissableContent = () => {
 return (
 <Dismiss render={
 dismiss => <Content dismiss={dismiss} /> // will be different every render
 } />
 )
}

In this example, every time DismissableContent renders, it generates a new value for the render prop, and this fails the shallow comparison done by React.PureComponent.

If it is important to you that this doesn’t happen — e.g., maybe Dismiss contains some intensive code — you could fix it by moving the render prop value into a named function, like this:

class Dismiss extends React.PureComponent {
 ...same implementation as above
}

const DismissableContent = () => {
 const content = (dismiss) => <Content dismiss={dismiss} />

 return (
 <Dismiss render={content} />
 )
}

This will ensure that the content value becomes an instance of the DismissableContent component and is not regenerated on every render.

Explore why these caveats have led devs to start using custom Hooks over render props.

If you want a playground to practice with render props, you can fork the CodeSandbox below and try to get a practical feel for working with the pattern.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now