React Readability Analysis Of Inline Conditional Rendering

August 01, 2021 - 9 minutes

Readable React

Being able to show UI elements conditionally to users is an essential part of making any React application interactive and more than just a static website. Based on different user interactions, content streams, and application states, your React application will have to render different elements to the user. One of the ways to implement this is through inline conditional rendering, which is a common practice in React development. But with how essential this type of code is, have you ever considered just how readable your inline conditional rendering code actually is?

Making sure that your code is highly readable is important in a lot of different ways. It makes the lives of other developers easier when they have to read, review, or maintain your code in the future. Being able to deliver high-quality readable code directly saves an engineering team time and effort at different moments, and thus holds tremendous implicit value.

A developer will make difficult code work, but a good developer will make difficult code look easy.

The importance of readable code is especially applicable to code patterns that occur frequently in your codebase as the impact is felt tenfold, like inline conditional rendering for React projects. In this article, I will go over the two most common approaches to implement inline conditional rendering: using the AND operator and the ternary operator.

We will discuss the advantages and drawbacks in terms of readability, and the use cases for every approach. This information will provide you with a solid foundation on how to implement inline conditional rendering in your React components in a readable manner. After this article, you will be able to apply these approaches, identify when your code declines in readability, and keep more complex constructions readable by building on top of this knowledge.

Using The AND Operator

The most popular and most used approach to implementing inline conditional rendering is through the AND operator. For this, the AND operator is used to verify a condition. Then, we specify the behaviour for the if branch. Then, if the condition evaluates to true, the branch is triggered and an element is rendered. Generally, it would look as follows:

const Card = ({ imageUrl }) => {
	return (
		<div className="card-container">
			{imageUrl && <Thumbnail url={imageUrl} />}
			<CardBodySection />
		</div>
	);
}

The biggest advantage of using the AND operator for inline conditional rendering is that it doesn’t require a lot of code. It is very short and concise. This reason alone already helps with the code readability. The fewer code developers have to read through during refactoring and future maintenance, the fewer opportunities will occur in which they get confused by the code.

In line with being short and concise, the AND operator also keeps your code very compact. Because of its non-verbosity, using it will save space in both the horizontal and vertical directions. This will greatly help other developers when reviewing your code in merge requests. Reviewing is often done on platforms like Github and Gitlab, which means that they happen in the browser. In the browser, there is barely any IDE support and space is limited. Being able to keep your code compact makes it easier for others to go through your code on these platforms and thus benefits the readability.

Logic wise, the biggest advantage is that the AND operator only describes one case. Nothing more, nothing less. In their mental modal, developers only have to account for the condition and the if branch while reading through the code. Using the AND operator gives an implicit signal that there is no else branch.

A major drawback to using the AND operator for inline conditional rendering is the fact that it relies on short-circuiting, an implicit JavaScript behaviour. When JavaScript evaluates an AND expression and the first operand evaluates the false, then the result of the first operand is returned and the evaluation of the second operand is entirely skipped. This concept is called short-circuit and it is crucial knowledge to understand how inline conditional rendering with the AND operator works.

This requirement heavily affects the maintainability and readability of this approach, as there is always the implicit assumption that the reader already understands this concept. Despite how likely this is in the context of React development, it’s not a guaranteed given. In cases where someone isn’t aware of short-circuiting, the caused confusion when reading the code can be significant.

The biggest drawback of using this approach lies in how the AND operator communicates to React what it should render. If the condition holds, then the second operand is treated as the if branch and used as the return value from the AND expression. This value is then used and rendered by React. In the context of inline conditional rendering, the second operand will be an element as that is what React expects.

But there will be cases where the condition doesn’t hold. In those cases, the resulting value of the condition (the first operand) is returned to React because of how short-circuiting works. This is fine because React understands that certain values should not be actually rendered to the DOM. These are falsy values like undefined, null, false. React will ignore these values and skip rendering them altogether.

The problem is that not all falsy values will be ignored by React, which can lead to unexpected rendering behaviour when the user is not aware of it. An example is the integer value 0, which is a falsy value that will be rendered by React as an actual value. A common pitfall for this is when checking for the length of an array.

// The AND operator below will return `0` if the tags array is empty,
// which will result into "0" being rendered on the screen.
const Card = ({ tags }) => {
	return (
		<div className="card-container">
			<CardBodySection />
			{ tags.length && <CardTagsSection tags={tags} /> }
		</div>
	);
}

This unexpected behaviour can cause a lot of confusion if the developer isn’t aware of the concept of short-circuiting and how React handles falsy values. This unexpected issue is something you always have to be aware of when encountering inline conditional rendering through AND operators, which greatly decreases the readability of this approach.

Lastly, a situational drawback that is often overlooked to using the AND operator is related to the required effort to read conditional expressions. Processing conditional expressions is not a trivial task and extending them in any way only makes it more difficult. This also applies from a readability perspective.

Using the AND operator to handle inline conditional rendering makes the implementation very easy. If you need the opposite case of a conditional, then you can just flip it with the NOT operator. This is also what happens very often, but this does exactly what we just discussed. Namely, adding another layer on top of the existing condition and thus decreasing the readability. Although it can be considered a minor drawback, the effect on readability can add up in larger numbers.

Summary

  • ✅ Short and concise.
  • ✅ Keeps things compact.
  • ✅ Great a describing one case.
  • ⛔ Relies on implicit JavaSript behaviour called short-circuiting.
  • ⛔ Can lead to unexpected issues when rendering certain falsy values in React.
  • ⛔ Handling the opposite case requires flipping the conditional.

Using The Ternary Operator

An alternative approach that is often used for inline conditional rendering is making use of the ternary operator. In this approach, the ternary operator is used to verify a condition. Inside the ternary, you define the behaviour for both the if and else branch. Then, based on the result of the condition, the appropriate branch is triggered to return an element to render. Generally, it would look as follows:

const Card = ({ imageUrl }) => {
	return (
		<div className="card-container">
			{imageUrl ? <Thumbnail url={imageUrl} /> : null}
			<CardBodySection />
		</div>
	);
}

The biggest advantage in terms of readability to using the ternary operator for inline conditional rendering is its explicitness. When using the ternary operator, you always have to specify both the if and the else branch. This means that for readers of your code, it’s always explicitly clear what is expected from the conditional rendering code.

While people will commonly think about the scenario where both branches return something to render, it equally holds for the scenario where only one branch is relevant. Just like in the above example with the else branch, using the ternary operator makes it explicitly clear to the reader that nothing is expected in one of the two branches. This degree of explicitness for both cases reduces ambiguity and confusion, thus benefitting the readability.

Another benefit to the ternary operator is that you can always use the same logic structure. A ternary expression always starts with the condition, followed by the if branch, and lastly the else branch. Because it always specifies both cases, there’s never a need to flip conditionals. This means that the order and structure of the ternary expression are always the same, and no additional logic layer is added on top of the existing condition. When other developers go through your React code and encounter a conditional render, they know it always reads in the same, most natural way. This makes it easier for them to get through your code.

The last benefit is related to handling different branches of the same condition at different places. Sometimes, the different branches of an inline conditional render are not in the same DOM location. Take the following scenario as an example:

const ComponentWithIconPlacement = ({ renderIconLeft }) => {
	return (
		<div className="container">
			{ renderIconLeft ? <Icon /> : null }
			{ someContent }
			{ renderIconLeft ? null : <Icon /> }
		</div>
	);
}

Here we have a component that accepts a boolean renderIconLeft prop, renders some content, and renders an icon either left or right from the content based on the mentioned prop. Based on the value of renderIconLeft, the icon has to be rendered at a different place in the DOM.

Using the ternary operator makes it easier to spot opposite cases that are related to each other. Due to fact that the logic structure can always remain the same and that the conditions don’t have to be flipped, the conditional rendering expressions are very similar and thus easy to match with its counterpart.

Based on the above example: after initially reading the first ternary operator, you might be left wondering what the expected behaviour is when renderIconLeft does not apply. Without having to flip conditions or look for related statements, you only have to look for ternary operators with the same condition but handle the opposite case. In this case, that is handled immediately after the content. Because the same logical expression is used, it’s very easy to find and match them, and determine what the behaviour is for the opposite case.

The main drawback to using the ternary operator for inline conditional rendering is its verbosity. By default, it requires a lot more code, which means that developers have to read and try to understand more code. This introduces more opportunities that can cause confusion, which makes it harder for developers to read through the code. This gets even worse when the ternary expressions become more complex like when larger conditions are necessary, the branches require more code, or ternary operators are nested. Combining these factors will have a significant compounding effect on the readability of your code.

Another drawback to using the ternary operator is again related to its verbosity. Because of it, a ternary operator can take up a lot of space both horizontally and vertically. How much vertical and horizontal space a ternary expression takes up directly affects the readability of the code. This means that the readability of ternary expressions can highly fluctuate dependent on the reader’s screen width, text wrapping configuration, and formatter print width. While this is not an unsolvable issue, having this dependency is suboptimal and requires time and effort to be addressed.

// The following two components are exactly the same, but have a totally
// different level of readability due to formatting.

// 1. One-liner with larger print width.
const Card = ({ imageUrl }) => {
	return (
		<div className="card-container">
			{imageUrl ? <Thumbnail url={imageUrl} rounded placeholder={false} {...someMoreProps} /> : null}
			<CardBodySection />
		</div>
	);
}

// 2. Multi-liner with smaller print width like a lot of default settings.
const Card = ({ imageUrl }) => {
  return (
    <div className="card-container">
      {imageUrl ? (
        <Thumbnail
          url={imageUrl}
          rounded
          placeholder={false}
          {...someMoreProps}
        />
      ) : null}
      <CardBodySection />
    </div>
  );
};

Summary

  • ✅ Explicit for both the if and else case.
  • ✅ Can always use the same logic structure (condition-if-else) without flipping conditions.
  • ✅ Easier to spot the opposite case if it’s located somewhere else.
  • ⛔ Very verbose, especially in more complex cases.
  • ⛔ Readability is affected by screen width and the formatter print width.

Final thoughts

In this article, we discussed two approaches to handling inline conditional rendering in your React components, either using the AND operator or the ternary operator. Both have their advantages, drawbacks, and use cases in terms of readability. This information provides you with a solid foundation on how to implement inline conditional rendering in your React components in a readable manner. You will be able to apply these structures, identify when your code declines in readability, and keep more complex constructions readable by building on top of this knowledge.


After graduation, my career is entirely centered around learning and improving as a developer. I’ve began working full time as a React developer and I’ll be blogging about everything that I encounter and learn during this journey. This will range from improving communicational skills in a technical environment, becoming a better developer, improving technical skills in React and JavaScript, and discussing career related topics. In all of my posts, the focus will be on my personal experiences, learnings, difficulties, solutions (if present), and also flaws.

If you’re either interested in these topics, more personalised technical stories, or the perspective of a learning developer, you can follow me either on Twitter or at Dev to stay up to date with my blogposts. I’m always learning, so stay tuned for more stories! 🎉