The art of conditional rendering: Tips and tricks for React and Next.js developers
Tags: engineering, opensourcesecurity
Published: 2023-10-31 02:00:43

Conditional rendering is a technique used in web applications to selectively render a component out of a set of candidates based on some condition, such as user authentication status, user privilege, or application state. It can also be used to implement a wide range of core React UI concepts, such as client-side routing and lazy loading.


In this article, you'll learn about the benefits of conditional rendering, how it differs from conditional routing, and how to implement both in [React](https://react.dev/), [Next.js](https://nextjs.org/), and [Remix](https://remix.run/).


Benefits of conditional rendering in React
------------------------------------------


In apps made using React or React-like frameworks, utilizing conditional rendering comes with a host of benefits, including faster load times.


The load time of a web application is influenced by the Document Object Model (DOM) size of the page being loaded. Keeping too many elements, especially those that are not displayed unless the user scrolls down, may lead to [an unnecessarily large DOM size](https://developer.chrome.com/en/docs/lighthouse/performance/dom-size/) and reduce your application's performance.


In this situation, [lazy loading](https://www.geeksforgeeks.org/what-is-lazy-loading/), a popular technique used to defer the loading of resources until needed, can be implemented to defer rendering a component unless the user scrolls down to bring it into the viewport. One of the most popular lazy loading libraries in React, [react-lazyload](https://github.com/twobin/react-lazyload), makes use of conditional rendering to render components only when they are scrolled into the viewport of the user's browser or, in other words, are visible to the user.


In addition to performance, conditional rendering can also help improve user experience. It enables you to customize your UI based on the user's properties, such as authentication state (*ie* a login button for unauthenticated users and a logout button for authenticated users in the same place on the screen) and access privilege (*ie* editing controls/detailed analytics for owners of content, such as tweets, while showing limited information for non-owners).


Moreover, conditional rendering can help you gracefully handle client-side operations, such as data fetching and communication with the backend. You can conditionally render a loading bar while an operation is carried out or hide an empty list while data is loaded from a remote source.


Conditional rendering also enables client-side routing in single-page React apps, such as the [react-router](https://www.npmjs.com/package/react-router) package.


In summary, it's safe to say that conditional rendering is one of the most important techniques used in React.


Conditional rendering vs. conditional routing
---------------------------------------------


When discussing conditional rendering, the topic of conditional routing often comes up. Conditional routing is similar to conditional rendering in that it controls what the user gets to see based on conditions such as the user or application state. However, instead of rendering or destroying components, it navigates the user from one route (or page) to another. This is useful from an access control perspective.


Web apps can be accessed directly via URLs. This means that the URL of a page inside of your app, regardless of whether the page is public, can be captured and retained by users. In the case of privileged pages (nonpublic pages that are only accessible to users with certain roles, such as personal profile/preferences pages), the URLs can be misused to try to gain access to privileged content.


With the help of conditional routing, a React app can determine if an incoming request for a page render has the right privilege to access the route. If the privilege is not found, the render is denied, and the request is redirected to another page. The page the user is redirected to often asks to authenticate or informs the user that access has been denied.


For example, to experience conditional routing in action, try navigating to <https://mail.google.com/mail> in a new incognito window. When you do, you'll be redirected to the Google sign-in page. That's conditional routing working behind the scenes.


However, conditional routing in an SPA is not a guarantee for security. Even before your (single page) React app redirects unauthenticated users away, it still contains the contents of the privileged page in its JavaScript source code. This could lead to incidents of unauthorized access to privileged data. Therefore, it's best to set up authentication-related conditional routing server side using frameworks such as Next.js and Remix.


Implementing conditional rendering in React
-------------------------------------------


Now that you know more about conditional rendering, in the following section, you'll learn how to implement it in a React app using one of the fundamental concepts of programming — the `if…else` statement.


To get started, create a new React app by running the following commands:



```
npx create-react-app conditional-rendering-react
cd conditional-rendering-react
npm install
```

Once the app is ready, paste the following code into the `src/App.js` file:



```
import { useState } from "react"

// Define two components
const LoginButton = (props) => {
  return <>
  You are logged out
  Log In
  
}

const LogoutButton = (props) => {
  return <>
  You are now logged in
  Log Out
  
}

function App() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use an if...else statement to render one out of two components based on the login state
  if (loggedIn)
    return 
  else
    return 
}

export default App;
```

This code has inline comments to explain how everything has been implemented.


Next, try running the app using the following command:



```
npm start
```

You'll see a text and a button on the screen when you navigate to the app URL (*ie* `http://localhost:3000`):


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-logged-out-2.jpg)
Try clicking on the **Log In** button to change the state and trigger a re-render. You'll notice that both the text and the button are changed with those applicable to a logged-in user:


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-logged-in-2.jpg)
And that's it! That's how easy it is to implement conditional rendering in React. Next, you'll see how to do it in a Next.js app.


Implementing conditional rendering and conditional routing in Next.js
---------------------------------------------------------------------


Next.js is an opinionated, production-focused, open source, React-based framework that helps you to build highly performant web apps. It was released by [Vercel](https://vercel.com/) in 2016 and has since grown to become one of the top React-based frameworks in the web development industry.


Out of the box, Next.js supports server-side rendering and static rendering, and it provides easy-to-use solutions for common development problems, such as data fetching, routing, and server runtimes.


### How to implement conditional rendering in Next.js


Because the syntax for JSX is consistent across Next.js and React, you can apply conditional rendering techniques in Next.js just as you would in React.


To get started in Next.js, create a new Next.js app by running the following command:



```
npx create-next-app conditionals-next
```

Follow the on-screen prompts to create your app. You can choose the default options (except for TypeScript, where you’ll need to select **No**). Once the project is created, paste the following code in the `pages/index.js` file:



```
import { useState } from "react";

// Define two components
const LoginButton = (props) => {
  return <>
    You are logged out
    Log In
  
}

const LogoutButton = (props) => {
  return <>
    You are now logged in
    Log Out
  
}

export default function Home() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use the ternary conditional operator to render one out of two components based on login state
  return loggedIn 
  ?  
  : 
}
```

This code contains inline comments to explain what's happening at each step. A notable change here compared to the React example is that this example makes use of the ternary conditional operator in JavaScript to choose one out of two components when rendering. You can run the app using the following command:



```
npm run dev
```

Once the app starts, you'll see something like this at [http://localhost:3000](http://localhost:3000/):


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-logged-out-black.jpg)
Once again, you can try clicking on the **Log In** button to change the state:


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-youre-logged-in-black.jpg)
### How to implement conditional routing in Next.js


Implementing conditional routing in Next.js 10+ is also easy. To get started, create a new `profile.js` file in your `conditionals-next/pages` directory and paste the following code snippet in it:



```
// This is the profile page
const Profile = () => {
  return This is your profile
}

// This function is called on the server side before the page is rendered
// Redirecting from this method means that the user will not see the page even for a flash of a second (which often happens in client-side redirection)
export async function getServerSideProps(context) {
    const allowAccess = false; // This is usually deduced from the authentication state of the user

    if (!allowAccess) {

        console.log("Redirecting to home...")

        return {
            redirect: {
                permanent: false,
                destination: "/"
            }
        }
    } else
        return { props: {} }
}

export default Profile
```

Then try running the app again and navigating to <http://localhost:3000/profile>. You will be instantly redirected to the home page (*ie* the `/` route). You can check the terminal to find the redirection log:



```
# ... other output here
wait  - compiling...
event - compiled client and server successfully in 109 ms (199 modules)
wait  - compiling /profile (client and server)...
event - compiled client and server successfully in 114 ms (202 modules)
Redirecting to home...
```

If you change the value of `allowAccess` to true, you can view the profile page:


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-your-profile-black.jpg)
Implementing conditional rendering and conditional routing in Remix
-------------------------------------------------------------------


Remix is another growing React-based framework for building web apps that focus on web standards and help you build better user experiences. Remix became [open source in 2021](https://changelog.com/news/remix-is-now-open-source-eZjN) and has become a strong competitor of Next.js.


Remix is a fully server-side rendered framework and offers features such as built-in error boundaries and transition handling.


### How to implement conditional rendering in Remix


Since Remix is based on React, you can implement conditional rendering in Remix similar to how you did in React and Next.js.


To get started, create a new Remix project by running the following command:



```
npx create-remix@latest
```

Follow the prompt to create your project. Make sure to choose JavaScript over TypeScript. Once the project is created, paste the following code snippet at `app/routes/index.jsx`:



```
import { useState } from "react";

// Define two components
const LoginButton = (props) => {
  return <>
    You are logged out
    Log In
  
}

const LogoutButton = (props) => {
  return <>
    You are now logged in
    Log Out
  
}

export default function Index() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use a switch statement to render one of two components based on login state
  switch(loggedIn) {
    case true: return 
    case false: return 
    default: return null
  }
}
```

This code snippet has inline comments to explain what's happening at each step. Instead of using an `if...else` statement or a ternary conditional operator, this example uses a switch statement to decide which component to render.


Similar to the previous examples, here's what the home page will look like at `http://localhost:3000`:


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-youre-logged-out-white.jpg)
As you've done previously, you can try clicking on the **Log In** button to change the state:


![](https://res.cloudinary.com/snyk/image/upload/v1698425600/blog-conditional-render-youre-logged-in-white.jpg)
### How to implement conditional routing in Remix


Similar to how you implemented server-side conditional routing in Next.js, you can implement it in Remix as well. Create a new file, `profile.jsx`, in the `conditionals-remix/app/routes` directory and paste the following code in it:



```
import { redirect } from '@remix-run/node'

const Profile = () => {
    return This is your profile
}

export const loader = () => {
    const allowAccess = false; // This is usually deduced from authentication state of the user

    if (!allowAccess) {
        console.log("Redirecting to home...")

        return redirect('/')
    }

    return null
}

export default Profile
```

A [loader function](https://jfranciscosousa.com/blog/typing-remix-loaders-with-confidence#:~:text=Loaders%2C%20what%20are%20they%3F,app%2Froutes%2Findex.tsx) is used here instead of the `getServerSideProps` function since Remix supports loaders for loading data in components while they are being rendered on the server (which is similar to what `getServerSideProps` can do for Next.js). In addition, a `redirect` utility function (similar to the Next.js `redirect` function) is returned to initiate a redirect when `allowAccess` is set to `false`.


Navigate to <http://localhost:3000/profile>, and once again, you'll be instantly redirected to the home page (*ie* the `/` route). You can check the terminal to find the redirection log:



```
# ... other output here
💿 Rebuilding...
💿 Rebuilt in 100ms
GET / 200 - - 17.179 ms
Redirecting to home...        #=== here it is
GET /profile 302 - - 7.107 ms
GET / 200 - - 6.564 ms
```

If you change the value of `allowAccess` to `true`, you'll be able to view the profile page:


![](https://res.cloudinary.com/snyk/image/upload/v1698425601/blog-conditional-render-your-profile-white.jpg)
Additional considerations for conditional rendering
---------------------------------------------------


When working with conditional rendering, you may wonder how it impacts the virtual DOM. Do the various methods of conditional rendering perform equally? Does creating a new component or Higher Order Component (HOC) to abstract away the details of the conditional rendering impact your app's DOM (and performance)? In this section, you'll learn the answers to these questions.


### Conditional rendering and JSX


The way in which you implement conditional rendering does not have an impact on your app's DOM and performance. Consider the following example:



```
if (loggedIn)
  return Log Out
else
  return Log In
```

You can also write it like this using the ternary conditional operator:



```
return {loggedIn ? "Log Out" : "Log In"}
```

Both of these will reduce to one of the two components (*ie* the `LoginButton` and `LogoutButton`) being returned by the parent component and will create the exact same change in the DOM. You may argue that both of these could return different instances of the button tag, but that's simply not true. This is because JSX elements, such as buttons and other tags, aren't instances. They are merely a description or a skeletal outline of your final HTML structure.


As long as you return the same elements in the same structure, the impact on the DOM will be the same. This means you can freely abstract away rendering behind components as long as the JSX structure is the same.


This also gives birth to interesting situations, such as a single state being shared by two components that are exactly the same in structure, where only one of them is conditionally rendered in the same place in the DOM. You can learn more about this [on the React website](https://react.dev/learn/preserving-and-resetting-state#same-component-at-the-same-position-preserves-state), which shows how large an impact a simple ternary operator in JSX can have on your app's functioning.


### Nested components and conditional rendering


Nesting the definition of components inside the components is [considered](https://stackoverflow.com/questions/52372040/is-it-an-anti-pattern-to-define-a-function-component-inside-the-render-functio) an anti-pattern. One of the main reasons behind this is that a nested component is always recreated when its parent component rerenders, regardless of its position in the DOM tree. This behavior is different from what you saw before because the nested component's definition in the runtime gets recreated on the rerender of the parent component. This is somewhat similar to a new instance of the nested component's definition being created, therefore causing this issue.


Conclusion and further resources
--------------------------------


While conditional rendering looks (and is) quite simple at a quick glance, it's a very powerful technique to improve the performance and user experience of React apps. In this article, you learned about its benefits and how it's different from conditional routing, as well as how to implement it in React, Next.js, and Remix.


To learn more about [conditional rendering](https://react.dev/learn/conditional-rendering) and [how React handles the state](https://react.dev/learn/preserving-and-resetting-state) across renders, make sure to check out [the new React docs](https://react.dev/learn).


When working on React apps, you'll often need to select and install npm packages to add additional functionalities (which include routing and state management, among others). [Snyk Advisor](https://snyk.io/advisor) can help you find the right npm package for your project while maintaining tight security standards for your app. Also, check out our [list of 10 security best practices](https://snyk.io/blog/10-react-security-best-practices/) you should follow when working with React.


[Snyk](https://snyk.io/) can also be used as an [IDE extension](https://snyk.io/platform/ide-plugins/) to find insecure code in React codebases and can help you fix any security vulnerabilities in [open source dependencies](https://snyk.io/product/open-source-security-management/).