Pages, Layouts, and Components

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

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.

Page with layout

Creating nested layout

Optionally, you can create a nested layout structure by defining layout.jsx for individual routes. For example,

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

nested layout

src/app/layout.jsx

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

jsx
1export default function DashboardLayout({ children }) {
2  return (
3    <>
4      <div>Dashboard navigation</div>
5      {children}
6    </>
7  );
8}

src/app/dashboard/page.jsx

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:

Nested layout

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:

text
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

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

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.