Cleaning up ternaries in React

Complex ternaries are messy in JSX. In this post, I guide you through a cleaner alternative.

To write clean React, you need to write clean JSX. One of the biggest offenders of this is the ternary expression which allows you to write an inline if statement:

<div>
{ loggedIn ? <LoggedIn /> : <LoggedOut /> }
</div>

This example is clean and easy to understand, but it’s not very flexible. For instance, you’d end up with this mess if you wanted to add another condition:

{
loading
? <Loading />
: (
loggedIn
? <LoggedIn />
: <LoggedOut />
)
}

In practice, we often break ternary expressions into multiple lines because we need to add children to our components:

<div>
{
loggedIn ? (
<div>
{/* A bunch of child components */}
</div>
) : (
<div>
{/* A bunch of child components */}
</div>
)
}
</div>

These components will sometimes need props and classNames and styles and click handlers… oh my. So once you add an else if to this conditional statement, things get a little messy.

{loading ? (
<div>
<PlaceholderData foo={bar} />
<div className="p-6 rounded some-background-color">
{/* more child elements */}
</div>
</div>
) : loggedIn ? (
<div>
<UserData foo={bar} baz={qux} onChange={() => {/* ... */}} />
<div className="p-6 rounded">
{/* more child elements */}
</div>
</div>
) : (
<div>
<LogInModal baz={qux} onSubmit={handleLogIn} />
<div className="p-6 rounded" style={{ backgroundImage: "..." }}>
{/* more child elements */}
</div>
</div>
)}

In my opinion, any time you have a multi-line ternary, you should split it into multiple components. Take the loading/logged in/logged out example. Each of these UIs should be split into its own components: Loading, LoggedIn, and LoggedOut. Then, the ternary should be extracted into a fourth component that holds all the rendering logic. For this example, we’ll call it “Conditional”, but you can rename it depending on the purpose it serves in your application. For instance:

// Extract the ternary into its own component.
function Conditional() {
const { loading, loggedIn } = usePageStatus()

if (loading) {
return <Loading />
} else if (loggedIn) {
return <LoggedIn />
} else {
return <LoggedOut />
}
}
// Use this where you would've put the ternary.
<Conditional />

This takes more lines of code than the ternary, but it’s so much easier to read and understand. If you come back a year later to add a fourth state, you’ll know exactly what to do; if you need to add another view with more complex logic (like “logged in as paid user” or “logged out with a certain parameter in the URL”), you can simply add to the if statement.

There’s still an elephant in the room: props. If your components rely on props from the parent, you can assemble them into an object and spread them into each of the conditional UIs:

const props = {
foo: "foo",
bar: "bar",
baz: "baz",
qux: "qux"
}

// ...

<Conditional props={props} />
function Conditional({ props }) {
const { loading, loggedIn } = usePageStatus()

if (loading) {
return <Loading {...props} />
} else if (loggedIn) {
return <LoggedIn {...props} />
} else {
return <LoggedOut {...props} />
}
}

And if you need to pass props down multiple levels of components (also called “prop drilling”), you can make this even cleaner by using Context. I have a post that teaches you how to set that up here, as well as how to update context from your child components:

Updating React Context inside child components

So there you have it: a way to remove complexity from JSX. Once you embrace it, React’s props and component system make it easy to write cleaner code.

About the author

I'm Mark Thomas Miller, a full stack engineer and designer currently working at ConvertKit. (We're hiring!) People like Arnold Schwarzenegger, Lindsey Stirling, and Tim Ferriss use features I've built to connect with their fans. I'm currently geeking out about Svelte, mechanical keyboards, and minimalist UI design, and replaying Ocarina of Time.