Search code examples
javascriptreactjsreact-routerreact-router-dom

How to conditionally render two components or change two outlets when a route is hit in React Router?


I have a React application where I'm using React-Router. In my layout component, I have a structure like this:

import React from 'react';
import Header from './components/Header/Header';
import Footer from './components/Footer/Footer';
import { Outlet } from 'react-router-dom';

function Layout() {
  return (
    <>
      <Header />
      <Outlet />
      <Footer />
    </>
  );
}

export default Layout;

Now, when a particular route is hit, I want to either conditionally render two different components or change two outlets. Is it necessary to use nesting for achieving this, or is there a way to have two outlets directly?

Additionally, I'm facing an issue with the isActive property in my NavLink. I want a div to be visible or show when the route is active. Here's the code for my NavLink:

<NavLink
  to="/"
  onClick={handleHomeClick}
  className={({ isActive }) =>
    `block py-2 pr-4 pl-3 duration-200 ${isActive ? "text-orange-700" : "text-gray-700"} border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 hover:text-orange-700 lg:p-0`
  }
  aria-current="page"
>
  HOME
  {isActive && (
    <div className="absolute top-full left-1/2 transform -translate-x-1/2 w-2 h-2 mt-1 border-2 border-yellow-500 bg-black rotate-45" />
  )}
</NavLink>

but this is giving error isActive is not defined. I tried searching for it online, but didn't get anything.


Solution

  • When a particular route is hit, I want to either conditionally render two different components or change two outlets.

    You can do that on that specific route. Use a Ternary operator to conditionally render components on the element prop. Also, each layout route can render only one outlet at-a-time.

    Example:

    <Routes>
      ...
      <Route element={<Layout />}>
        ...
        <Route
          path="/somePath"
          element={someCondition
            ? <ComponentA />
            : <ComponentB />
          }
        />
        ...
      </Route>
      ...
    </Routes>
    

    You could, of course, abstract this into a component as well.

    const SomeComponent = () => {
      ...
    
      return someCondition
        ? <ComponentA />
        : <ComponentB />
    };
    
    <Routes>
      ...
      <Route element={<Layout />}>
        ...
        <Route path="/somePath" element={<SomeComponent />} />
        ...
      </Route>
      ...
    </Routes>
    

    I'm facing an issue with the isActive property in my NavLink. I want a div to be visible or show when the route is active.

    The NavLink component can also take a function for the children prop similar to the className (and style) prop.

    <NavLink
      to="/"
      onClick={handleHomeClick}
      className={({ isActive }) =>
        `block py-2 pr-4 pl-3 duration-200 ${isActive ? "text-orange-700" : "text-gray-700"} border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 hover:text-orange-700 lg:p-0`
      }
      aria-current="page"
    >
      HOME
      {({ isActive }) => isActive && (
        <div className="absolute top-full left-1/2 transform -translate-x-1/2 w-2 h-2 mt-1 border-2 border-yellow-500 bg-black rotate-45" />
      )}
    </NavLink>