Three ways to conditionally render components with React and JSX

Sometimes, you want to render a UI component only if a certain condition is true. Perhaps a feature is enabled for a subset of users only (such as administrators or premium accounts), or maybe part of the UI can be toggled on and off in the app’s settings. I’d like to share a few different ways to conditionally render a component with React and JSX that I’ve found useful. Which one you should choose depends on the situation. Sometimes, one approach may be the best tool for the job. Other times, it may come down to personal preference.

Getting started

Let’s consider a basic web app layout. We have a main content section and a sidebar. There’s also a special toolbar that only administrators should be able to see. This toolbar should be hidden from regular users.

For our code examples, we’ll be inside the render() method of the parent component of these three sections. Here’s what it might look like without any conditional rendering:

render()
{
    return (
        <AdminToolbar/>
        <PrimaryContent/>
        <Sidebar/>
    );
}

Let’s assume that this parent component has a prop named isAdmin that we’ll use to figure out if AdminToolbar should render or not.

Now that we’ve established our context, let’s look at our various options for conditional rendering.

Approach 1: A variable and an if() statement

The most basic approach is to store an instance of our AdminToolbar component in a variable, but only if the isAdmin prop is true.

let adminBar = null;
if(this.props.isAdmin)
{
    adminBar = <AdminToolbar/>;
}

Then, we’ll use our ability to embed expressions in JSX to reference the variable inside our JSX.

return (
    { adminBar }
    <PrimaryContent/>
    <Sidebar/>
);

React has no problem dealing with a null value in JSX. If the adminBar variable is null, React will skip over it without rendering anything.

Approach 2: Return null from component’s render() method

In the next example, we leave it up to the AdminToolbar component to decide on its own whether to render or not. Let’s give AdminToolbar its own isAdmin prop so that we can pass our value down one more level:

render()
{
    return (
        <AdminToolbar isAdmin={this.props.isAdmin}/>
        <PrimaryContent/>
        <Sidebar/>
    );
}

Then, in the render() method of the AdminToolbar component, we’ll return null if the prop is false.

class AdminToolbar extends React.Component
{
    render()
    {
        if(!this.props.isAdmin)
        {
            return null;
        }
        return (
            <div>
                { /* omitted for simplicity */ }
            </div>
        );
    }
}

Similar to the previous example, React understands that it shouldn’t render anything when a component returns null from its render() method.

Approach 3: Ternary operator (a ? b : c)

As a final option, we can take advantage of the shorthand nature of the ternary operator to embed everything directly inside our JSX:

render()
{
    return (
        <div>
            { this.props.isAdmin ? <AdminToolbar/> : null }
            <PrimaryContent/>
            <Sidebar/>
        </div>
    );
}

In my opinion, this approach works best when we don’t have any props to pass to the component (or a very small number of props) because we can keep everything on one line. Usually, if we need to pass many props to a component, it’s better to place the props on multiple lines. A ternary operator gets more difficult to read in that situation, and I don’t think it’s particularly obvious how to format this type of complex expression over multiple lines.

This following example seems somewhat reasonable, I guess:

render()
{
    return (
        <div>
            {
                this.props.isAdmin ?
                (
                    <AdminToolbar
                        name="Administrator"
                        needsUpdate={true}/>
                )
                : null
            }
            <PrimaryContent/>
            <Sidebar/>
        </div>
    );
}

However, I don’t think I’d be happy with this formatting if the condition were longer — with multiple parts separated by && and || operators. Similarly, if the final expression were more complex than null, I feel like the mental overhead to understand how each expression fits into the ternary operator would become too high. In my opinion, if the ternary operator needs to be split into multiple lines, our first approach (with the variable and an if() statement) is a much better choice. For one-liners, though, the ternary operator is a pretty compelling option.

One, two, or three?

Which approach do you prefer? Do you know about any other ways to conditionally render a component in React and JSX? If I forgot to include anything, let me know in the comments!

react-loadable and TypeScript error: Property ‘render’ is missing in type ‘{ loader: () => Promise

The react-loadable library provides an excellent way to load React components on demand while displaying a temporary view until they finish loading (such as a spinner animation). Recently, I tried using react-loadable with TypeScript, and the compiler reported a strange and unexpected error. Here’s the journey I took figure out what was wrong, and how I fixed it.

You can see that my code below is very simple, but I was getting an error, for some reason:

import * as React from "react";
import * as Loadable from "react-loadable";
import MyLoadingComponent from "./MyLoadingComponent";

const LoadableMyComponent = Loadable(
{
	loader: () => import("./MyComponent"),
	loading: MyLoadingComponent
});

The full compiler error appears next (with a modified file path to make it more readable):

Argument of type '{ loader: () => Promise<typeof '/path/to/MyComponent' is not assignable to parameter of type 'Options<any, typeof '/path/to/MyComponent">'.
  Type '{ loader: () => Promise<typeof "/path/to/MyComponent' is not assignable to type 'OptionsWithRender<any, typeof '/path/to/MyComponent'.
    Property 'render' is missing in type '{ loader: () => Promise<typeof '/path/to/MyComponent'.

In my editor, the error begins at the opening curly brace { and ends at the closing curly brace }, so it’s clear that there’s something wrong with the options object that I’m passing in. However, it was not immediately obvious to me why the TypeScript compiler didn’t like my object.

The error says that a render property is missing. My first thought was that I was missing a render() method in a component somewhere. However, both MyComponent and MyLoadingComponent are React components with proper render() methods defined.

The code for MyComponent.tsx appears below:

import * as React from "react";

export default class MyComponent extends React.Component<any>
{
	render()
	{
		return <div>MyComponent has loaded</div>
	}
}

And here’s the code for MyLoadingComponent.tsx:

import * as React from "react";

export default class MyLoadingComponent extends React.Component<any>
{
	render()
	{
		return <div>Loading...</div>;
	}
}

As you can see, render() is not missing in my components. So where exactly should a property named render be defined?

After reading through the react-loadable documentation, I figured out that the options object can be one of a few different variations, and one of them requires a render property. The TypeScript compiler thinks I’m trying to use the variation where this property is required…

…but I’m not.

According to the following comment from the type definitions, I need to meet the following requirements to make the render property optional:

If you do not want to provide a render function, ensure that your function is returning a promise for a React.ComponentType or is the result of import()ing a module that has a component as its `default` export.

You can see that the function that I pass to the loader property should work. I’m loading a module containing MyComponent as the default export, and it extends React.Component.

loader: () => import("./MyComponent")

After some trial and error, I determined the real problem. The React component that I pass to the other option (loading) isn’t accepted because I typed its “props” as any:

class MyLoadingComponent extends React.Component<any>

According to the type definitions for react-loadable, I should use ReactComponent.<Loadable.LoadingComponentProps> instead. I would have expected the TypeScript compiler to figure out that Loadable.LoadingComponentProps can be assigned to any, but I guess not.

Anyway, here’s how I should have defined MyLoadingComponent instead:

import * as React from "react";
import * as Loadable from "react-loadable";

export default class MyLoadingComponent extends React.Component<Loadable.LoadingComponentProps>
{
	render()
	{
		return <div>Loading...</div>;
	}
}

It took me quite some time to figure out what exactly was wrong with my code. The TypeScript compiler was fixated on that render property, which led me down the wrong path. I wish the compiler would have realized that my React.Component was using type any for its props instead of the Loadable.LoadingComponentProps. I wonder if there’s a better way to define these options in the type definitions so that the wrong type for props would have taken precedence over the missing (but not really) render property?

Migrating an Apache Flex app to React: Milestone 1

Last year, I mentioned that I’m migrating an Apache Flex application to JS/HTML/CSS. At the time, I was still very early in the process. I knew that I’d be using TypeScript and React by that point, but I was still evaluating various UI component libraries. In addition to learning and prototyping, I also had to port a lot of code that wasn’t part of the UI, so I haven’t had much to share about my progress until recently.

Yesterday, I deployed the first bit of code from the new version of my app Logicly to production. The original codebase included something that I call the “Logicly Viewer”, which is a limited version of the project that can be embedded in HTML to load a file in read-only mode. I created it so that I could embed a real circuit simulation into the documentation to help make various concepts more clear. Unlike the main editor, nothing in Logicly Viewer can be added, deleted, or dragged around. However, you can interact with certain components to update the simulation and watch signals propgate. I knew right away that the Logicly Viewer would make a perfect first milestone because I wouldn’t need to implement Logicly in its entirely. All of the work on this milestone will act as a foundation for future milestones, and I get to deploy something to production that real users will be able to see today while I continue to work on the rest of the project. That really helps keep me motivated!

I’ve embedded the Logicly Viewer below, with a simulation of a half adder:

(Click/tap the switches on the left side to see the simulation update)

Logicly simulates logic gates, which are low level constructs in computers and electronics that perform boolean math (operations on 1s and 0s). The simulation keeps track of the current state of a circuit, which includes the signal being output from each logic gate and where that signal gets passed next. It was relative easy to port the simulation from ActionScript to TypeScript, since the languages are so similar. However, I wanted to avoid introducing any bugs, so I ended up writing a ton of automated tests at the same time. The original implementation of the simulation in ActionScript didn’t have any tests, so writing them took a non-trivial amount of time because I had to start from scratch. It was worth it, though, because it caught a few bugs that I had accidentally introduced into the new TypeScript version.

Once the simulation was up and running, I started working on the “wires” that can be dragged between components to connect things in the simulation. You can see them in the embedded viewer above — they’re the bezier curves that are drawn between components. Implementing these as React components was an interesting challenge because it requires more access to the HTML DOM than most people using React typically need. Additionally, React’s typical data flow doesn’t play well when many components need to interact with each other from various levels of the tree. I ended up using Redux so that I could use its Provider component to connect the components with data. As for visuals, I ended up using SVG. It’s really nice to be able to use SVG right alongside HTML with JSX.

The code below basically shows how I use my wires library in React/JSX:

<WiresProvider>
	<WireSurface wireFactory={this.createWireRenderer}/>
	<div>{components}</div>
</WiresProvider>

The WiresProvider is a Redux Provider component with an internal store, and it passes the data down to any components that are interested. The WireSurface is where the wires are drawn. As I mentioned, I implemented the wires as SVG bezier curves, but the WireSurface supports custom implementations that draw wires differently, if needed. The wires get the data about the connections from the WiresProvider. The components added to the <div> that appears after the WireSurface are the logic gates and other components that get displayed by the viewer/editor. These may contain child components called “terminals” where the wires start and end their connections.

The logic gates and other components are also drawn using SVG. I was able to export SVG files from my original vector art created in Adobe Animate CC. I made a few tweaks to simplify the generated SVG, but the output was actually pretty decent overall. Between JSX and CSS, modifying paths and colors in SVG at runtime turned out to be very easy. I’m excited to explore SVG further because I feel like modern web apps could really benefit, but it seems to be underutilized today.

Obviously, the Logicly Viewer needs to be able to open files created by the full version of Logicly. The underlying file format is XML, but it’s also compressed using the deflate algorithm. I ended up using the pako library to inflate the data to get the raw XML string, and I used @rgrove/parse-xml to parse the XML into JS objects. Originally, I had worried that file parsing would be a major pain. However, this ended up being one of the easiest tasks that I worked on in this project! The @rgrove/parse-xml library produced a very easy to understand object tree.

What’s next?

In the next milestone, I will start implementing the full editor with things like drag-and-drop and selection. In fact, I already have some of the drag-and-drop features implemented, since I wanted to be sure that was easy enough to implement with React before I committed. I’ll be using the excellent react-dnd library.

The next thing that I will deploy to production will be the web demo of Logicly that allows people to try the app in their browser. It currently runs on Adobe Flash Player, and #Flash2020 looms ever closer. This web demo doesn’t support saving files, printing, or opening multiple files at the same time, but everything else works the same way as the desktop app. It’ll be more work than the Logicly Viewer, I think, but it’s also another subset that allows me to deploy more to production before the whole project is completed. After that, I’ll start digging into Electron to build desktop apps for Windows, macOS, and Linux. Then, Logicly will be ready for the future, and I can explore new possibilities, like mobile apps, Chromebooks, and more!