In many web designs, there are often UI components and design elements that are shared across multiple pages, such as headers, footers, navigation menus, and so on.
Hardcoding these elements can be problematic for future maintenance. So, in this lesson, we are going to discuss how to create reusable components in Next.js.
Creating layout for the pages (layout.jsx)
First of all, Next.js allows us to define a layout that will be shared across all pages.
Navigate to the app
directory and create a layout.jsx
file. The UI elements that needs to be shared across all pages, such as the navbar and footer, can be placed here, and all the pages will automatically extend to it.
src/app/layout.jsx
1// Import CSS files
2import "./globals.css";
3
4export default function RootLayout({ children }) {
5 return (
6 <html lang="en">
7 <body>
8 <nav>This is a navbar</nav>
9 {children}
10 <footer>This is a footer</footer>
11 </body>
12 </html>
13 );
14}
This layout at the root level of app
is required by Next.js, and it should always contain the <html>
and <body>
tags. The layout should accept a children
prop, which will be populated by the corresponding page.jsx
during rendering.
As an example, we created a navbar and a footer in the above layout demo, and they should be shared between all the pages. Try navigating to different pages, and the navbar and footer will persist.
Creating nested layout
Optionally, you can create a nested layout structure by defining layout.jsx
for individual routes. For example,
1app
2├── blog
3│ └── page.jsx
4├── dashboard
5│ ├── layout.jsx <===== Layout for /dashboard/*
6│ ├── posts
7│ │ └── page.jsx
8│ └── users
9│ └── page.jsx
10├── favicon.ico
11├── fonts
12├── globals.css
13├── layout.js <===== Global layout
14└── page.js
Notice that we have two layout.jsx
files, one at the root level (/
), which is required by Next.js, and another one for /dashboard/*
.
In this case, the root layout app/layout.jsx
will wrap around app/dashboard/layout.jsx
, which then wraps around the webpages app/dashboard/posts/page.jsx
and app/dashboard/users/page.jsx
.
src/app/layout.jsx
1import "./globals.css";
2
3export default function RootLayout({ children }) {
4 return (
5 <html lang="en">
6 <body>
7 <nav>This is a navbar</nav>
8 {children}
9 <footer>This is a footer</footer>
10 </body>
11 </html>
12 );
13}
src/app/dashboard/layout.jsx
1export default function DashboardLayout({ children }) {
2 return (
3 <>
4 <div>Dashboard navigation</div>
5 {children}
6 </>
7 );
8}
src/app/dashboard/page.jsx
1export default function Dashboard() {
2 return <div>This is the dashboard page.</div>;
3}
Open the browser and visit /dashboard
, the following page should be rendered:
Breaking the UI into reusable components
However, not all components needs to be shared across all pages. Certain UI elements are used in some pages, but not the others.
In this case, it is recommended to create a dedicated folder for these components. For example:
1src
2├── app
3│ ├── api
4│ ├── blog
5│ ├── dashboard
6│ ├── favicon.ico
7│ ├── fonts
8│ ├── globals.css
9│ ├── layout.jsx
10│ └── page.jsx
11└── components <=====
12 └── sidebar.jsx <=====
src/components/sidebar.jsx
1export default function Sidebar() {
2 return <aside>This is a sidebar</aside>;
3}
The components can then be imported into any pages you want.
src/app/page.jsx
1import Sidebar from "@/components/sidebar";
2
3export default async function Home() {
4 return (
5 <div>
6 <Sidebar />
7 </div>
8 );
9}
But, take a closer look at how we are importing the sidebar component, and notice that the path starts with @
.
Recall that in the first lesson where we set up the project, the initialization program asked us a question: Would you like to customize the import alias (@/*
by default)?
We set the import alias to default, which means the character @
maps to the src
directory, and @/components/sidebar
points to the sidebar component we just created.