The React Render Prop: How to build reusable components, reduce maintenance costs, and save time

The React Render Prop: How to build reusable components, reduce maintenance costs, and save time

·

3 min read

In this post, we’re going to take a look at what a React render prop is and how it can help you build better components.

A surefire way to make your code more maintainable is to leverage code reusability. Render props are one way to help you do that. They let you share core business logic between React components. Learning how to use render props will help you build flexible components with less code.

Instead of wasting time on maintenance, you can focus on feature development.

A render prop is a function that returns a React element

React components take props that can be of any value.

A common pattern is to use component composition to render custom content in child components. This lets you build complex layouts within the context of a wrapper component.

function Panel(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      Some cool content
    </div>
  );
}

function App() {
  return <Panel title={<p>I'm a title</p>} />;
}

The Panel component takes a title prop and renders it in an h1 tag. The title prop can be any valid React component. This leads to more flexible solutions.

While this is a good use case, there’s no reason that the title prop needs to be a React element.

Let’s set the title prop to a function that returns a React element instead

function Panel(props) {
  return (
    <div>
      <h1>{props.title()}</h1>
      Some cool content
    </div>
  );
}

function App() {
  return <Panel title={() => <p>I'm a title</p>} />;
}

Not much has changed in this example.

The difference is the title prop is a function that returns the React element (i.e. title is a render prop). While the title prop needs to be called in the Panel component now, the output should be the same between the two examples.

If the output is the same then why bother using a function?

The benefit is that we can pass arguments to the function. In other words, the Panel component can define some logic and pass that to the title function. This lets us build custom components based on logic defined in the Panel.

The Panel component can share logic with its wrapper

function Panel(props) {
  const [isExpanded, setIsExpanded] = React.useState(false);
  const toggleExpanded = () => setIsExpanded(prev => !prev);

  return (
    <div>
      <h1>{props.title(toggleExpanded)}</h1>
      {isExpanded && "Some conditionally cool content"}
    </div>
  );
}

function App() {
  return (
    <Panel
      title={(toggleExpanded) => {
        return (
          <div>
            <p>I'm a title</p>
            <button onClick={toggleExpanded}>Toggle Expanded</button>
          </div>
        );
      }}
    />
  );
}

This time the Panel component has collapsible content. When isExpanded is true we show the content. The key part is that the Panel doesn't update isExpanded directly.

Instead, we pass toggleExpanded as an argument to the title render prop.

With the toggleExpanded parameter, we can define any UI experience we want. In this case, we bind the expand and collapse behavior to a button's onClick handler.

But that isn't required.

We can change this to fit our needs. For example, we can toggle the expanded state when a user hovers over the title instead. Since we have control over the title prop, we're free to do anything we want.

A Todo List demo built with a render prop

The following is an example of how to use a render prop to build a reusable todo list.

Conclusion

Render props help you build more maintainable components.

You can use them to share and reuse logic between different components. Since you can return any React element they’re extremely flexible. It puts the control in the consumer’s hands, so you don’t have to account for every edge case yourself.

The next time you build a reusable component, consider using a render prop.