Master The Layouts In React JS. Control Layout From Any Page - DEV Community.
Tags: javascript, beginners, tutorial, react
Published: 2024-05-30 14:21:36

Hi Developers🙋‍♂️,

We are going to talk about the **Layouts** in **React JS**. I will share with you an advanced version of creating layouts and the layout elements like the sidebar, header, etc. can be controlled from any page. 

**Let's Explore🥳**

## Before we start, what is the Layout?
A layout component is a reusable component that defines the structure of your application's user interface. It typically includes common UI elements like headers, footers, sidebars, and navigation menus. By centralizing these elements in a layout component, you ensure consistency across different pages of your application​.

**Be ready** with your React JS project with a form and install [React-Router-Dom](https://reactrouter.com/en/main/start/tutorial) to validate the form. Confused??, No worries.

**Step 1: Installing React JS**
I am going with Vite to install the React JS. Simply run the following command to install React JS.

```js
npm create vite@latest my-react-app --template react
```
*replace the my-react-app with your project name.

change the directory to the project folder.
```
cd my-react-app
```

 Install the required dependencies by running

```
npm install
```
**Step 2: Installing react-router-dom**
Install the `react-router-dom` using the following command

```
npm install react-router-dom
```

**T**hat's all you need, We are **Good to Go.🟢**

I will walk you through the basic and better methods to implement the layouts in React JS. 


## **Method 1: Basic Method To Create Layouts**

When you are learning React JS, it's okay to go with the basic method, But this method has less control over the layout component.
 
**Step 1: Setup the Routes**
After installing the `react-router-dom`, you need to set up the routes to create multiple pages in a React JS application. So create a `jsx` file called `routes.jsx` in the `src` directory. As well as create `pages` folder inside `src` directory, and create two file `Dashboard.jsx` and `ChangePassword.jsx` (took some random pages as example).

Then the folder structure will be like this

```
my-vite-react-app/
├── node_modules/
├── public/
│   └── vite.svg
├── src/
│   ├── App.css
│   ├── App.jsx
│   ├── index.css
│   ├── main.jsx
│   ├── routes.jsx
│   └── pages/
│       ├── Dashboard.jsx
│       └── ChangePassword.jsx
├── .gitignore
├── index.html
├── package.json
├── README.md
└── vite.config.js
```
**You can set up your folder structure according to your project, I am using a random structure for this example.**

Now, add the routes for `Dashboard` and `ChangePassword` in `routes.jsx`. Then you will be able to navigate and render those pages without layout.

```jsx
import { createBrowserRouter } from "react-router-dom";
import Dashboard from './pages/Dashboard';
import ChangePassword from './pages/ChangePassword';

export const routers = createBrowserRouter([
    {
      path: "/dashboard",
      element: <Dashboard/>,
    },
    {
      path: "/change-password",
      element: <ChangePassword />,
    },
  ]);
```

In `App.jsx`

``` jsx
import { RouterProvider } from "react-router-dom";
import routers from './routes'

function App() {
    return <RouterProvider router={router} />
};
```

Okay, Now you are good with routes. Let's create `<Layout />`.

**Step 2: Create Layout Component**
The `Layout` can change according to the design. I will demonstrate with two layout components `SideBar` and `Header`.

So, create a folder in `src` called `components` and add `index.jsx`, `Sidebar.jsx`, and `Header.jsx`.

And, you will have a folder structure as follows.

```
my-vite-react-app/
├── node_modules/
├── public/
│   └── vite.svg
├── src/
│   ├── App.css
│   ├── App.jsx
│   ├── index.css
│   ├── main.jsx
│   ├── routes.jsx
│   ├── pages/
│   │   ├── Dashboard.jsx
│   │   └── ChangePassword.jsx
│   └── components/
│       ├── index.jsx
│       ├── Sidebar.jsx
│       └── Header.jsx
├── .gitignore
├── index.html
├── package.json
├── README.md
└── vite.config.js
```

Create a sidebar component in `src/components/Sidebar.jsx`

```jsx
import { useNavigate } from "react-router-dom";
import Dashboard from "../../../assets/icons/Dashboard";
import Settings from "../../../assets/icons/Settings";
import Pages from "../../../assets/icons/Pages";
import DummyLogo from "../../../assets/icons/DummyLogo";

const Sidebar = () => {
  const navigate = useNavigate();
  const menuItems = [
    {
      icon: <Dashboard width="20" />,
      title: "Dashboard",
      link: "/dashboard",
    },
    {
      icon: <ChangePassword width="20" />,
      title: "Change Password",
      link: "/change-password",
    },
  ];
  const navigateToHref = (link) => {
    navigate(link);
  };

 return (
    <>
      <div
        className="hidden lg:block relative w-full max-w-[300px] bg-white-500  h-full border-r-[1px] border-primary border-opacity-30 shadow-sm"
        style={{ backgroundColor: "white" }}
      >
        {/* <img src="/sigi.png" alt="LOGO" className="w-[130px]" /> */}
        <div className="flex gap-3 justify-center items-center h-[65px] ">
          <DummyLogo width={35} height={35} />
          <p className="font-bold ">
           BRAND NAME
          </p>
        </div>
        <ul className="w-full px-5 py-9">
          {menuItems.map((data, index) => {
            return (
              <li className="mb-4 flex justify-center" key={index}>
                <button
                  type="button"
                  onClick={() => navigateToHref(data.link)}
                  className={` w-full flex justify-start items-center px-6 py-2 rounded-[7px] hover:shadow-sm text-sub_text ${
                    window.location.pathname == data.link
                      ? "bg-primary text-white shadow-sm fill-white"
                      : "hover:bg-primary hover:bg-opacity-10 fill-border_color"
                  } `}
                >
                  {data.icon}
                  <span className="ml-4 text-[14px] font-semibold tracking-wider ">
                    {data.title}
                  </span>
                </button>
              </li>
            );
          })}
        </ul>
      </div>
  )
};
```
Tailwind CSS is used for the basic styling.


Let me add a basic `Header` component in `src/components/Header.js`.

```jsx
// Replace this with image
import ProfileImage from '../../assets/profileImage.png'
const Topbar = () => {

 return (
 <div className="shadow-sm border-b-[1px] border-primary  border-opacity-10">
      <div className="w-full  flex justify-between items-center gap-4 p-4">
        <div>
          <button type="button" onClick={cycleOpen}>
            <MenuIcon width="25" className="fill-primary stroke-primary" />
          </button>
        </div>
        <div className=" flex justify-center items-center gap-x-4">
          <button className=" px-4 py-3 rounded-md bg-[#FFFAF1] relative">
            <Notification width="15" />
            <div className="rounded-[50%] w-[8px] h-[8px] bg-red-500 absolute top-1 right-1"></div>
          </button>
          <div>
            <img
              src={ProfileImage} 
              alt="profile image"
              className="w-[30px] h-[40px] rounded-[10px]"
            />
          </div>
          <div className="flex justify-between items-center gap-6">
            <div className="flex flex-col items-start">
              <h1 className="text-text_color text-[14px]">
                John Doe
              </h1>
              <h1 className="text-border_color text-[13px]">Admin</h1>
            </div>
           
          </div>
        </div>
      </div>
    </div>
);
}
```

>  THE SIDEBAR AND HEADER THAT I HAVE USED IS A RANDOM CODE THAT I HAVE. FEEL FREE TO REPLACE THE CODE WITH YOU SIDEBAR AND HEADER.

> !!!! Important thing is next is next component.

Inside the `src/components/Layout/index.js`, This will be used as `<Layout />`.
```jsx
import Topbar from "./Topbar";
import Sidebar from "./Sidebar";

const Layout = () => {
  return (
    <div className=" flex overflow-hidden h-screen ">
      <Sidebar />
      <div className="w-full h-full overflow-hidden">
        <Topbar />
        <div className="px-8 pt-8 pb-[150px] w-full h-[91%] overflow-auto bg-secondary_bg">
          <Outlet />
        </div>
      </div>
    </div>
  );
};

export default Layout;
```

**What is Outlet ?**
In `react-router-dom`, an Outlet is a component used to render child routes within a parent route. It's part of the React Router v6 API, introduced as a replacement for the <Switch> component used in React Router v5 and earlier versions.

When you define routes in your application, you typically nest them within parent routes. The parent route acts as a container for its child routes. The Outlet component serves as a placeholder where child routes will be rendered.

After creating the `outlet`, you need to modify the routes as follows.

```jsx
// src/routes.jsx

// ....
// ...
import Layout from './comonents/Layout';
const router = createBrowserRouter([
    {
      path: "/",
      element: <Layout />,
      children: [
        {
          path: "/dashboard",
          element: <Dashboard />,
        },
        {
          path: "/change-password ",
          element: <ChangePassword />,
        }
      ]
     }
   ]);
```
Wooohh, We are created a `<Layout/>` component with a basic method.

This is the method used by a lot of developer. I this is the method that you are also using, I have a question for you.

**Suppose, If you don't want the `<Header />` rendered in the `/change-password` route how do you implement with this `<Layout />`?**  

Thinking about a context🤔? 
Stay Tuned, I have something different.

**Method 2: Advanced Version of Layout**
We will keep the folder structure from the Method 1.

**Step 1: Modify the routes file**

`src/routes.jsx`

```jsx
import { createBrowserRouter } from "react-router-dom";
import Dashboard from './pages/Dashboard';
import ChangePassword from './pages/ChangePassword';
import Layout from './components/Layout';

// THIS WILL GENERATE A COMBINED VERSION OF LAYOUT AND PAGE COMPONENTS FOR ROUTES
const generateRoutesWithLayout = (routeArray) => {
  return routeArray?.map((route) => {
    if (route.children?.length > 0) {
      return {
        path: route.path,
        children: generateRoutesWithLayout(route.children),
      };
    }
    if (route.element?.type?.getLayout) {
      return {
        path: route.path,
        element: route.element?.type?.getLayout(route.element),
      };
    }
    return {
      path: route.path,
      element: route.element,
    };
  });
};

const router = [
         {
           path: "/dashboard",
           element: <Dashboard/>,
         },
        {
           path: "/change-password",
           element: <ChangePassword />,
         },
       ];

export const routers = createBrowserRouter(generateRoutesWithLayout(router));
```
`**generateRoutesWithLayout**` is an important function to note. This function will invoke the getLayout function by passing the page component as a parameter. The `getLayout` function will return the page with Layout and render in the UI according to the props, passed into the `<Layout />`


**Step 2: After This, Modify The `<Layout/>`**.

```jsx
import Sidebar from "./Sidebar";
import Header from "./Header";

const Layout = ({ children, sidebar= { show: true }, header= { show: true } }) => {
  return (
    <div className=" flex overflow-hidden h-screen ">
      { sidebar.show && <Sidebar /> } 
      <div className="w-full  h-full overflow-hidden relative">
        { header.show && <Header  /> }
        <div className="px-4 pt-4 pb-24 w-full h-[calc(100vh-152px)] overflow-auto ">
          {children && children}
        </div>
      </div>
    </div>
  );
};

```

**That's it**🙌😁, the small update will bring the ability to control the `Layout` component.

**Step 3: Let's Learn How To Use This <Layout />**
To use this layout inside your pages like `Dashboard`.

You need to add a function in your page component called `getLayout` before you export your page component.

Let's see in the code `<Dashboard />`

```jsx
import Layout from '../components/Layout';
const Dashboard = ()=>{
return (
    // you dashboard UI
    )
};

Dashboard.getLayout = (page)=> {
  return (
    <Layout>{page}</Layout>
  )
}
export default Dashboard
```

That's it you have the dashboard with the layout.

Suppose you don't want to render the `<Header/>` in `/change-password` screen.

``` jsx
import Layout from '../components/Layout';
const ChangePassword = ()=>{
return (
    // you Change Password UI
    )
};

ChangePassword.getLayout = (page)=> {
  return (
    <Layout header={{ show: false }}>{page}</Layout>
  )
}
export default ChangePassword;
```
Now, you have control over `<Header/>` in the change password page.

## **Things You Can Do With This Setup**

- You can conditionally render the `Header` or `Sidebar` or any other 
  layout elements.
- You can set up breadcrumbs dynamically by using props (Rendering of 
  different breadcrumbs on each page is simple with this method).
-  Dynamically render the menu items in `sidebar`. Just need to receive 
   an array of menu items and extra props in `Layout` and pass it to `Sidebar`, 
   then it's simple to loop over the props and render it in UI.

Anddd, There are a lot of things you can do with this Layout setup.


## **Conclusion**
We saw the difference between the basic Layout component and the better Layout component. Method 2 is the recommended way of approach to create the Layout.

**Cool😎**, I think you learned something new today.

## **About Me**
I am Sajith P J, Senior React JS Developer, A JavaScript developer with the entrepreneurial mindset. I combine my experience with the super powers of JavaScript to satisfy your requirements.

## **Reach Me Out **

- [LinkedIn](https://www.linkedin.com/in/sajith-p-j/)
- [Instagram](https://www.instagram.com/dev_.sajith/)
- [Website](https://sajith.in/)
- [Email (sajithpjofficialme@gmail.com)](mailto:sajithpjofficialme@gmail.com)

Thanks !!!🙌