
Conditional Rendering
Conditional rendering is rendering elements that may or may not be displayed on a web page depending on certain conditions or events. These conditionals are decision paths in your code, something like an if statement. We use conditional rendering to render elements when a particular decision path is hit. A good example of conditional rendering is a credit card information form that only appears when you choose credit card as your payment option. Conditional rendering allows us to use standard JavaScript conditional statements (such as if and else) to dynamically choose which components to display in our application and which ones to hide.
For example, let's look at this example:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (<p className="App">
<button>Click me to show the rest!</button>
<p>
I am the content that should be hidden by default!
</p>
</p>
);
}
}
export default App;
This should just give us a button on a page and a completely visible p element below that we want to be hidden by default when the user visits this app, as shown in the screenshot that follows:

Figure 3.1: Basic app
In React, there are two primary ways to tackle conditional renders:
- Inline in JSX
- Via a dedicated subrender function.
With Inline in JSX option, you might see something like this:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
const showMessage = true;
return (
<p className="App">
{ showMessage && <p>Hello World</p>}
</p>
);
}
}
export default App;
Here, you can see that we are just adding a conditional statement and only displaying the Hello World message when showMessage is true, literally in the same line as our JSX.
An example of a subrender function, that is, a dedicated function that checks a conditional and either returns the JSX code or null, would be this instead as shown below:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
renderMessage(show) {
if (show) {
return <p>Hello World</p>;
} else {
return null;
}
}
render() {
const showMessage = true;
return (
<p className="App">
{ this.renderMessage(showMessage) }
</p>
);
}
}
export default App;
In the following exercise, we will use these concepts to flesh out a more complex application using conditional rendering.
Exercise 3.01: Building Our First App Using Conditional Rendering
In this exercise, we are going to build a React application that has a button that shows and hides the content on the page on click, very similar to the preceding example. We will create a page with a button and some content in a p, but we will use conditional rendering to show and hide the content. This will also hinge on your learnings from the last chapter, as we will want to use state to appropriately show or hide the content.
- Start off by creating a new React application via the same npx create-react-app command we've run a few times now on the command line:
$ npx create-react-app exercise1
$ cd ./exercise1
$ yarn start
- Delete the contents of src/App.css but keep the file. In src/App.css, let's add some CSS to make the button a little larger and easier to click:
button {
margin: 0.25em;
height: 10ex;
font-weight: bold;
}
- Delete src/logo.svg since we will not be using that in this exercise.
- In src/App.js, start off by changing the React import statement at the top to also include {Component } as we will use a class component and will have an internal state to keep track of. Remove the reference to src/logo.svg since we will not need that logo either. Your import statements at the top now should just be:
import React, { Component } from 'react';
import "./App.css";
- Delete the existing App component and, instead, we will just want a single p and a button that just says Click to show the secret message!. Our new App component is as follows:
class App extends Component {
render() {
return (
<p className="App">
<button>Click to show the secret message!</button>
</p>
);
}
}
This should leave us with an application that looks like this:
Figure 3.2: App showing the button
- Build up the state for our application to keep track of whether we're showing or hiding the secret message.
We'll build out a constructor, as shown in Chapter 1, Getting Started with React, taking in the props argument and passing it in a call to the parent class's constructor, and then we'll build up a state that has an initial property in it called showSecret, which will default to false:
constructor(props) {
super(props);
this.state = {
showSecret: false
};
}
- Let's write up a conditional renderer now. You can choose to be as creative as you'd like with it:
secretMessage() {
return (
<p className="secret-message">
I am the secret message!
</p>
)
}
- Add the call to secretMessage() to your render() function's JSX so that the text will actually show up somewhere on the app:
render() {
return (
<p className="App">
<button>Click to show the secret message!</button>
{ this.secretMessage() }
</p>
);
}
This should give us an app that resembles the following screenshot (depending on what you chose for your secret message, of course):
Figure 3.3: App showing the secret message
- Let's add a new function called toggleSecretMessage() to start modifying the state.
This function will be responsible for toggling the state of the secret message display, so this should be relatively simple to write:
toggleSecretMessage() {
this.setState({
showSecret: !this.state.showSecret
});
}
We will want to call this function from our button since that will act as the trigger to show and hide the secret message. Since we'll be using an event handler in a button to call a function that relies on setting the state of the component, we'll also need to make sure we're appropriately binding this to the context of the component and not the event, so we'll need to include a bind statement.
- Let's do this by calling bind on the toggleSecretMessage function. Therefore, this.toggleSecretMessage (the function itself) becomes
this.toggleSecretMessage.bind(this)
We will throw bind into the onClick handler, as an inline bind statement is fine for any function where the bind only needs to happen once.
- Let's create our new render function as follows:
render() {
return (
<p className="App">
<button onClick={this.toggleSecretMessage.bind(this)}>
Click to show the secret message!
</button>
{ this.secretMessage() }
</p>
);
}
This allows React to know that when the button is clicked, the code being executed remembers that it is inside of the component we are building. That means that any references to this in our code will be replaced with the component instead of whatever called that function, like our onClick handler function.
- Render the secret message.
There are two approaches to this problem, and we will discuss the pros and cons of each. We will start with an inline conditional in JSX with a ternary operator associated with it:
{ this.state.showSecret ? this.secretMessage() : null }
This gives us a conditionally rendered secret message (when the browser refreshes or you navigate to the page, it will now be hidden by default), which is the correct behavior. We are using the ternary operator here to make the code a little cleaner and because if statements inside of JSX are not even syntactically correct. We return a null in case there are no values so that React knows not to attempt to add anything to the DOM. This is fine for very simple examples and can arguably help to keep your code clean, but there is another way for us to write this conditional rendering statement.
- Return back to our secretMessage() function definition. Add a guard clause to our function.
Note
A guard clause is a simple statement at the top of your function that verifies inputs, properties, or state, and returns early to avoid unnecessary work.
The new code for this function is as follows:
secretMessage() {
if (!this.state.showSecret) { return; }
return (
<p className="secret-message">
I am the secret message!
</p>
)
}
Note that at the top of that function, we now have a quick conditional that checks the state for showSecret. If it's false, we return nothing out of the function and call it a day. If it's true, we continue on with rendering the secret message.
- Call our render function and modify the JSX to instead just call this.secretMessage() without the inline conditional:
render() {
return (
<p className="App">
<button onClick={this.toggleSecretMessage.bind(this)}>
Click to show the secret message!
</button>
{ this.secretMessage() }
</p>
);
}
When you visit the page now (or when the browser refreshes), you should no longer see the secret message. This is nearly perfect, but there is a little more we can do. If you look at the text on the button, it just always displays Click to show the secret message!. If we are making elements on the page state-aware, we should apply the same rule throughout to maintain the consistency of our app.
- In the code for the button, we will use an inline conditional rendering statement to change what text is displayed to the user based on the component's state:
<button onClick={this.toggleSecretMessage.bind(this)}>
Click to { this.state.showSecret ? "hide" : "show" } the secret message!
</button>
Now when we visit the page, by default, we get the click to show button text and the hidden secret message, and when we click the button, we instead get the secret message and a click to hide text on the button instead. When we first load the page, we will see this:
Figure 3.4: App showing the button
And when we click on the button, we will get this:

Figure 3.5: App showing the secret message
With that, we have completed writing our first exercise with conditional rendering.