This site runs best with JavaScript enabled.

Use ternaries rather than && in JSX

Photo by Burst


What problems can happen when you use && to conditionally render content in JSX

What's wrong with this code?

1function ContactList({contacts}) {
2 return (
3 <div>
4 <ul>
5 {contacts.length &&
6 contacts.map(contact => (
7 <li key={contact.id}>
8 {contact.firstName} {contact.lastName}
9 </li>
10 ))}
11 </ul>
12 </div>
13 )
14}

Not sure? Let me ask you another question. What would happen with the above code if contacts was []? That's right! You'd render 0!

I shipped this mistake to production at PayPal once. No joke. It was on this page:

PayPal page with contacts

When a user came with no contacts, this is what they saw:

PayPal page with 0 instead of contacts

Yeah, that was not fun... Why is that? Because when JavaScript evaluates 0 && anything the result will always be 0 because 0 is falsy, so it doesn't evaluate the right side of the &&.

The solution? Use a ternary to be explicit about what you want rendered in the falsy case. In our case, it was nothing (so using null is perfect):

1function ContactList({contacts}) {
2 return (
3 <div>
4 <ul>
5 {contacts.length
6 ? contacts.map(contact => (
7 <li key={contact.id}>
8 {contact.firstName} {contact.lastName}
9 </li>
10 ))
11 : null}
12 </ul>
13 </div>
14 )
15}

What about this code? What's wrong here?

1function Error({error}) {
2 return error && <div className="fancy-error">{error.message}</div>
3}

Not sure? What if error is undefined? If that's the case, you'll get the following error:

1Uncaught Error: Error(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

Or, in production, you might get this:

1react-dom.production.min.js:13 Uncaught Invariant Violation: Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=f for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

The problem here is that undefined && anything will always evaluate to undefined.

So what gives?

Really, the problem in both of these cases is we're using && to do conditional rendering. Said differently, we're using && to do conditional argument passing. Remember, JSX is a simple syntactic abstraction over calling React.createElement. So it'd be like trying to write this:

1function throwTheCandy(candyNames) {
2 for (const candyName of candyNames) {
3 throwCandy(candyName)
4 }
5}
6
7throwTheCandy(candies.length && candies.map(c => c.name))

That wouldn't make any sense right? We've got our types all messed up. The reason React allows you to do this though is because rendering 0 is valid.

Luckily, TypeScript can help you avoid the second example of accidentally returning undefined. But on top of that layer of protection, how about we be more explicit about our intentions. If you want to do something conditionally, don't abuse the logical AND operator (&&).

It actually seems counter-intuitive because we often use && in an if condition to say: if both of these values are truthy, then do this thing, otherwise don't. Unfortunately for our use-case, the && operator also has this "feature" where if both values aren't truthy, it returns the value of the falsy one:

10 && true // 0
2true && 0 // 0
3false && true // false
4true && '' // ''

Use actual branching syntax

So I strongly suggest that rather than abusing && (or || for that matter) in your JSX, you use actual branching syntax features, like ternaries (condition ? trueConsequent : falseConsequent) or even if statements (and in the future, maybe even do expressions).

I'm personally a fan of ternaries, but if you'd prefer if statements, that's cool too. Here's how I'd do the contacts thing with if statements:

1function ContactList({contacts}) {
2 let contactsElements = null
3 if (contacts.length) {
4 contactsElements = contacts.map(contact => (
5 <li key={contact.id}>
6 {contact.firstName} {contact.lastName}
7 </li>
8 ))
9 }
10
11 return (
12 <div>
13 <ul>{contactsElements}</ul>
14 </div>
15 )
16}

Personally, I think the ternary is more readable (if you disagree, remember that "readable" is subjective and the readability of something has much more to do with familiarity than anything else). Whatever you do, you do you, just don't do bugs.

Conclusion

I'm well aware that we could've solved the contact problem by using !!contacts.length && ... or contacts.length > 0 && ... or even Boolean(contacts.length) && ..., but I still prefer not abusing the logical AND operator for rendering. I prefer being explicit by using a ternary.

To finish strong, if I had to write both of these components today, here's how I'd write them:

1function ContactList({contacts}) {
2 return (
3 <div>
4 <ul>
5 {contacts.length
6 ? contacts.map(contact => (
7 <li key={contact.id}>
8 {contact.firstName} {contact.lastName}
9 </li>
10 ))
11 : null}
12 </ul>
13 </div>
14 )
15}
1function Error({error}) {
2 return error ? <div className="fancy-error">{error.message}</div> : null
3}

Good luck!

Discuss on TwitterEdit post on GitHub

Share article
EpicReact.Dev

Get Really Good at React

Get yourself the most comprehensive guide to React for professional developers in the universe.

Blast Off

Write professional React.

Kent C. Dodds

Kent C. Dodds is a JavaScript software engineer and teacher. He's taught hundreds of thousands of people how to make the world a better place with quality software development tools and practices. He lives with his wife and four kids in Utah.

Join the Newsletter



Kent C. Dodds