DEV Community

Cover image for Master The Layouts In React JS. Control Layout From Any Page - DEV Community.
Sajith P J
Sajith P J

Posted on

Master The Layouts In React JS. Control Layout From Any Page - DEV Community.

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 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.

npm create vite@latest my-react-app --template react
Enter fullscreen mode Exit fullscreen mode

*replace the my-react-app with your project name.

change the directory to the project folder.

cd my-react-app
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies by running

npm install
Enter fullscreen mode Exit fullscreen mode

Step 2: Installing react-router-dom
Install the react-router-dom using the following command

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

That'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
Enter fullscreen mode Exit fullscreen mode

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.

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 />,
    },
  ]);
Enter fullscreen mode Exit fullscreen mode

In App.jsx

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

function App() {
    return <RouterProvider router={router} />
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Create a sidebar component in src/components/Sidebar.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>
  )
};
Enter fullscreen mode Exit fullscreen mode

Tailwind CSS is used for the basic styling.

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

// 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>
);
}
Enter fullscreen mode Exit fullscreen mode

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 />.

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;
Enter fullscreen mode Exit fullscreen mode

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 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.

// src/routes.jsx

// ....
// ...
import Layout from './comonents/Layout';
const router = createBrowserRouter([
    {
      path: "/",
      element: <Layout />,
      children: [
        {
          path: "/dashboard",
          element: <Dashboard />,
        },
        {
          path: "/change-password ",
          element: <ChangePassword />,
        }
      ]
     }
   ]);
Enter fullscreen mode Exit fullscreen mode

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

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));
Enter fullscreen mode Exit fullscreen mode

**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/>.

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>
  );
};

Enter fullscreen mode Exit fullscreen mode

That's itπŸ™ŒπŸ˜, the small update will bring the ability to control the Layout component.

Step 3: Let's Learn How To Use This
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 />

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

Dashboard.getLayout = (page)=> {
  return (
    <Layout>{page}</Layout>
  )
}
export default Dashboard
Enter fullscreen mode Exit fullscreen mode

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

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

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;
Enter fullscreen mode Exit fullscreen mode

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 *

Thanks !!!πŸ™Œ

Top comments (7)

Collapse
 
thomasbnt profile image
Thomas Bnt β˜•

Hello ! Don't hesitate to put colors on your codeblock like this example for have to have a better understanding of your code 😎

console.log('Hello world!');
Enter fullscreen mode Exit fullscreen mode

Example of how to add colors and syntax in codeblocks

Collapse
 
sajithpj profile image
Sajith P J

@thomasbnt Thank you for guiding me, Updated all the posts with code blocks.

Collapse
 
thomasbnt profile image
Thomas Bnt β˜•

Glad you liked my help!

Collapse
 
adnankattekaden profile image
AdnanKattekaden

congrats keep going on

Collapse
 
sajithpj profile image
Sajith P J

Thanks Dude.

Collapse
 
sudhansu_sekharprusty_a3 profile image
Sudhansu Sekhar Prusty • Edited

Hi!, I am getting some error

Module not found: Error: Can't resolve './Header' ?, I have the header.jsx included.
Is it possible to share the github repo for this ?

Collapse
 
sajithpj profile image
Sajith P J

Please make sure that the Header component exists.

Check the followings also

  1. Make sure that you have index.jsx file inside the Header folder.
  2. Make sure that you have a Header component inside the index.js.

The code is in a private repo for now. i will try to add the code in a public repo. The repo link will be shared.