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
*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
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
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 />,
},
]);
In App.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
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
.
// 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 />
.
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 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 />,
}
]
}
]);
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));
**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>
);
};
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
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;
Now, you have control over <Header/>
in the change password page.
Things You Can Do With This Setup
- You can conditionally render the
Header
orSidebar
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 inLayout
and pass it toSidebar
, 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)
Hello ! Don't hesitate to put colors on your
codeblock
like this example for have to have a better understanding of your code π@thomasbnt Thank you for guiding me, Updated all the posts with code blocks.
Glad you liked my help!
congrats keep going on
Thanks Dude.
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 ?
Please make sure that the Header component exists.
Check the followings also
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.