Understanding the layout file in Next.js
The basics of the layout file
In short the layout file in Next.js is a template that dictates how your pages are laid out. When you first create your app the installer automatically creates a layout.tsx or layout.js depending if you chose typescript of not. This his your root layout file and it is required.
Basic route file
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Production layout page example
It is in this file where you also setup additional requirements like context theme settings etc. Example page with custom Google fonts and added frontend cookie provider and context.
import type { Metadata } from "next";
import { Inter, Cormorant } from "next/font/google";
import { CookiesProvider } from 'next-client-cookies/server';
import { ContextProvider } from "@/components/shared/context";
import "./globals.css";
// change to add additional fonts
const inter = Inter({
subsets: ["latin"],
variable: '--font-inter',
});
const cormorant = Cormorant({
weight: ['400', '700'],
style: ['normal', 'italic'],
subsets: ['latin'],
display: 'swap',
variable: '--font-cormorant',
})
// changed to allow for dynamic metadata
export const metadata: Metadata = {
title: {
default: "NEXT.JS | The Boiler Plate",
template: "%s | NEXT.JS",
},
description: "Generated by create next app modified by Ray Noppe",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
{/* body updated to include font variables and also set full height and width which will help with flexbox layouts */}
<body className={`${inter.variable} ${cormorant.variable} font-sans h-full`}>
{/* CookiesProvider added to allow for client side cookies */}
<CookiesProvider>
{/* ContextProvider added to allow for global state management */}
<ContextProvider>{children}</ContextProvider>
</CookiesProvider>
{/* in reality you would use alternate layouts to control context zones instead of wrapping the whole children as that will make it client side. */}
</body>
</html>
);
}
Use layout page to control different parts of your app
Lets say you have an app that has three areas:
- Marketing/information pages (root layout) with a header and footer no sidebars
- A product area where you have the same header and footer plus a left side bar that has to display on all product pages (layout 1)
- A blog where all pages have to have a right sidebar with the archive visible on all pages (layout 2).
You can do this by grouping the pages using a folder names in parenthesis () or creating a sub folder as parent folder for sub pages and then creating a new layout.tsx file in this group along with your pages.
Note: the layout.tsx files placed in the () folders will inherit the settings from the root layout.tsx, so if for example you created context zones they will still be there.
...
▼ src
▼ app
► api
► mynewpage
▼ (group1)
▼ folder1
page.tsx
layout.tsx // layout whit left side bar
▼ (group2)
► folder2
page.tsx
layout.tsx // layout with right side bar
★ favicon.ico
# global.css
☣ layout.tsx
☣ page.tsx
► components
► lib
...
Route layout
import type { Metadata } from "next";
import { Inter, Cormorant } from "next/font/google";
import "./globals.css";
import Header from "@/components/shared/header";
import Footer from "@/components/shared/footer";
// change to add additional fonts
const inter = Inter({
subsets: ["latin"],
variable: '--font-inter',
});
const cormorant = Cormorant({
weight: ['400', '700'],
style: ['normal', 'italic'],
subsets: ['latin'],
display: 'swap',
variable: '--font-cormorant',
})
// changed to allow for dynamic metadata
export const metadata: Metadata = {
title: {
default: "NEXT.JS | The Boiler Plate",
template: "%s | NEXT.JS",
},
description: "Generated by create next app modified by Ray Noppe",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${inter.variable} ${cormorant.variable} font-sans h-full`}>
<Header classHearder="bg-red-100 flex space-x-3 px-3 items-center h-[42px]" />
<main className="flex h-[calc(100%-84px)] w-full">{children}</main>
<Footer />
</body>
</html>
);
}
Alternate layout 1
export default function AlternateLayout1({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<aside className=" w-[300px] bg-red-600 text-white p-3">Left bar</aside>
<section className=" flex-grow p-3">{children}</section>
</>
)
}
Alternate layout 2
import Header from "@/components/shared/header"
export default function AlternateLayout2({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<section className=" flex-grow p-3">{children}</section>
<aside className=" w-[300px] bg-red-600 text-white p-3">Right bar</aside>
</>
)
}
Passing params to the layout file
You can pass param(s) to the layout.tsx file, useful if you want to have a distinct layout for an individual product or for a user. I personally don’t see the need for this as you are limited by what a layout file can and can’t do.
- A few pointers:
- You must have a layout.tsx file in your root (/src/app) and it is should contain your <html> and <body> tags
- You have to use the Metadata API to control <head>, <title> and <meta> tags
- You can’t access search params (?area=admin&page=2) in a layout file
- You can’t access pathname as layouts are rendered on the server, you can however get it by adding a client component to your layout
I recommend using the layout file purely for design/UI proposes and for wrapping parts of your layout in context blocks. Do your logic in pages and components.