Passing Elements as Props in React

The middle ground between a props explosion and render props

David Barral
Nov 4 · 4 min read
Photo by Ricky Kharawala on Unsplash

In this story we are going to see a simple technique that allows you to write friendly customizable components with simple APIs, just by using the basic React building blocks: the element.

In my experience, a seasoned React programmer uses this technique naturally, but a newcomer struggles to grasp it. If you are the former, I won’t waste your time. This story is for the latter.

props explosion? Wut?

With props explosion (patent pending) I mean having props to customize the smaller components that build your component or having nested props. Let’s see this with an example.

Suppose you have an Alert component with a Button inside.

What can you do to make the inner Button customizable? If you are a React newcomer and read the docs you probably think the correct way of doing this is by passing props.

Fair enough, but what happens if we are dealing with a Confirm component with two buttons? You need to duplicate props and have some prefixing to avoid clashing.

As you can imagine this will only get worse as your component grows. This is what I meant with props explosion.

One way to tidy the code a little is by nesting props: one prop for each inner component with the necessary keys.

This fixes the clashing, but things are getting complex. What happens if I want to override just one nested prop? To support that we need to manually merge the props with the defaults. And, what happens if the Button also contains an Icon? Should you use a pair of new props (acceptButtonIcon, rejectButtonIcon)? Should you nest them inside the existing ones (acceptButton.icon)? Your prop types are going to be so much fun to read. By now, you should get my point.

The all or nothing approach: render props

Maybe you have read that a way to make your components really customizable, instead of falling into a prop hell, is using a render prop. Let’s try that. We’ll code three new versions of the Confirmation component.

First, one render prop per button:

Then, one render prop for all the buttons:

And, finally, a render prop for all the component:

The last one is kind of extreme. Confirm is a controlled component and it doesn’t make much sense. Imagine that Confirm is an uncontrolled component with its own state and behaviour and it will make more sense.

Render props are as powerful as it can get but they also come with their own nuances. They’re kind of an all or nothing approach. They let you change how you render the component but it’s not easy to reuse the “defaults”. It depends on what your render prop renders. In the previous examples we saw three levels, each allowing more flexibility but less reusability of the “defaults”.

However, this approach may seem overkill when we just want to customize the text of the Alert/Confirm buttons. There must be something we can do to find a middle ground.

The middle ground

As you guessed from the title of this story, there is a middle ground: passing elements as props.

This API is much more natural to just change the “clickable” parts of Confirm. No props explosion hell. No forcing a render prop API onto the user. This is one of that instances where cloneElement is the right tool.

Note: if you wonder why we are always passing the handlers when, in some instances, it would make sense to just use them directly, you are right. In this case it’s not mandatory. For example, you can do the following and not clone the onAccept/onReject.

Does Confirm need to wrap or do something with the handlers or is it’s just passing down the props? Do you need to overwrite or merge the className? Choose your API wisely.

Why the struggle?

I believe it’s JSX. Specifically, newcomers approach to JSX.

JSX has always been the most WTF thing about React and it has been thoroughly debated. It’s a very powerful tool but to the eye of an untrained developer can seem magical: “it’s HTML on your JS” or “it’s how you write your component render function”. It’s much less and much more.

Learn what JSX really is and how it works and the technic I just showed will become second nature to you.

You can read this section of the docs , this story by Asís García and this post by Kent C. Dodds to deepen your knowledge about using elements as props.

Trabe

We are a development studio. We use Java, Rails, and JavaScript. This is where we write about the technologies we use at Trabe.

Thanks to Asís García and Clara Dopico

David Barral

Written by

Co-founder @Trabe. Developer drowning in a sea of pointless code.

Trabe

Trabe

We are a development studio. We use Java, Rails, and JavaScript. This is where we write about the technologies we use at Trabe.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade