Definition (from Next.js documentation):
Layouts are used to define UI that is shared across multiple pages. They allow you to create consistent experiences by reusing the same structure (like headers, footers, and sidebars) across your application.
A layout can wrap one or more pages in the app and can be nested, enabling the creation of hierarchical layouts. Layouts are designed to be Server Components by default in Next.js, which makes them efficient for performance and static generation.
Folder structure:
app/
├── layout.tsx // Global layout
├── page.tsx // Home page
├── about/
│ ├── page.tsx // About page
app/layout.tsx
:export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>My Website Header</header>
<main>{children}</main>
<footer>My Website Footer</footer>
</body>
</html>
);
}
This layout wraps all pages in the app and applies a common structure: a header, main content area, and footer.
app/page.tsx
:export default function HomePage() {
return <h1>Welcome to the Home Page</h1>;
}
When visiting /
, the content from HomePage
will be displayed within the layout structure.
Definition:
Nested Layouts allow you to create multiple layers of layouts, where each layout applies to a subset of pages. A nested layout wraps another layout, enabling hierarchical designs and shared sections at different levels.
Nested layouts are especially useful when you need to apply different layouts to different parts of the site, such as having a main layout for the entire site and a nested layout for specific sections.
Folder structure:
app/
├── layout.tsx // Root layout
├── dashboard/
│ ├── layout.tsx // Dashboard layout
│ ├── settings/
│ │ ├── page.tsx // Settings page
│ ├── profile/
│ ├── page.tsx // Profile page
app/layout.tsx
:export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>My Website Header</header>
<main>{children}</main>
</body>
</html>
);
}
app/dashboard/layout.tsx
:export default function DashboardLayout({ children }) {
return (
<div>
<nav>Dashboard Sidebar</nav>
<div>{children}</div>
</div>
);
}
app/dashboard/settings/page.tsx
:export default function SettingsPage() {
return <h1>Dashboard Settings</h1>;
}
When navigating to /dashboard/settings
, the following layout structure will be rendered:
app/layout.tsx
) wraps the entire page.app/dashboard/layout.tsx
) wraps the SettingsPage
, applying the sidebar to all dashboard-related pages.Definition (from Next.js documentation):
Route Groups allow you to group routes together without affecting the URL structure. You can define layouts that apply to certain groups of pages using route groups.
Route Group Layouts are similar to normal layouts, but they apply to specific groups of routes that are organized into folders that don’t affect the URL.
Folder structure:
app/
├── (marketing)/
│ ├── layout.tsx // Layout for marketing pages
│ ├── home/
│ │ ├── page.tsx // Home page (accessible at '/')
│ ├── about/
│ ├── page.tsx // About page (accessible at '/about')
├── (dashboard)/
│ ├── layout.tsx // Layout for dashboard pages
│ ├── settings/
│ ├── page.tsx // Settings page (accessible at '/settings')
app/(marketing)/layout.tsx
:export default function MarketingLayout({ children }) {
return (
<div>
<header>Marketing Header</header>
<main>{children}</main>
</div>
);
}
app/(dashboard)/layout.tsx
:export default function DashboardLayout({ children }) {
return (
<div>
<nav>Dashboard Navigation</nav>
<main>{children}</main>
</div>
);
}
In this example:
(marketing)
route group, but this does not affect the URLs. The home
page is accessible at /
, and the about
page is accessible at /about
.(dashboard)
route group, so the settings
page is accessible at /settings
.()
does not affect the URL structure but can be used to apply shared layouts or components across a group of routes.layout.tsx
in any directory.()
folders) without affecting the URL structure.Each of these layout types provides a way to modularize your Next.js application, making it scalable and easy to maintain.
Next.js has a robust metadata API that allows developers to define meta tags for SEO, social media sharing, and other purposes at the page level. Starting from Next.js 13, Next.js uses App Router which introduces a new way to define static, dynamic, and async metadata.
Metadata in Next.js helps improve SEO and the overall performance of your app by controlling page title, description, Open Graph metadata for social sharing, and more.
Each type of metadata is integrated into Next.js’s App Router.
Static metadata is defined in a component at build time. This means that it remains the same throughout the application lifecycle and does not rely on any runtime data.
Static metadata is usually defined inside the metadata
object exported from a component file, like layout.tsx
or page.tsx
. Here’s how you can define static metadata:
// app/page.tsx
export const metadata = {
title: "Home | My Next.js App",
description: "Welcome to the home page of my Next.js app",
};
metadata
object and can be used on both pages and layouts.title
, description
, and keywords
are static values, ideal for simple pages where the content doesn’t change based on user interaction or external data.Dynamic metadata allows you to set metadata based on the runtime state or request. This is important when your page’s content and metadata depend on dynamic input, such as route parameters or query strings.
Dynamic metadata can be generated based on props, params, or even cookies.
Imagine a blog where the page’s metadata depends on the slug (unique identifier) of the blog post:
// app/blog/[slug]/page.tsx
import { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: { slug: string };
}): Promise<Metadata> {
const post = await getPostBySlug(params.slug);
return {
title: post.title,
description: post.excerpt,
};
}
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>{params.slug}</h1>;
}
params.slug
.generateMetadata
function allows you to use route parameters or query parameters to generate page-specific metadata dynamically.Async metadata in Next.js lets you fetch metadata asynchronously, often from external APIs or databases. This is beneficial when metadata depends on data that is fetched during runtime.
In Next.js 13, generateMetadata
can also be an asynchronous function to allow for async data fetching inside the metadata generation process.
Consider a page where the metadata is fetched from an external API, such as a headless CMS:
// app/product/[id]/page.tsx
import { Metadata } from "next";
async function fetchProductData(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`);
return res.json();
}
export async function generateMetadata({
params,
}: {
params: { id: string };
}): Promise<Metadata> {
const product = await fetchProductData(params.id);
return {
title: product.name,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
images: [
{
url: product.image,
},
],
},
};
}
export default function ProductPage({ params }: { params: { id: string } }) {
const product = fetchProductData(params.id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
generateMetadata
function can return a Promise
that resolves to metadata.Next.js offers several commonly used metadata fields that you can include in your metadata object.
export const metadata = {
title: "My Page Title",
description: "This is a description of my page",
keywords: ["next.js", "seo", "metadata"],
author: "John Doe",
viewport: "width=device-width, initial-scale=1.0",
};
For social media optimization (Facebook, Twitter, etc.), Open Graph metadata is essential.
export const metadata = {
openGraph: {
title: "My Awesome Site",
description: "A short description of my site",
url: "https://mysite.com",
siteName: "MySite",
images: [
{
url: "https://mysite.com/og-image.jpg",
width: 800,
height: 600,
alt: "Og Image Alt",
},
],
locale: "en_US",
type: "website",
},
};
Twitter-specific metadata can also be added:
export const metadata = {
twitter: {
card: "summary_large_image",
site: "@site_account",
creator: "@individual_account",
title: "Twitter Title",
description: "Twitter Description",
images: ["https://example.com/image.jpg"],
},
};
In Next.js, you can define metadata for entire sections of your app by exporting metadata from the layout.tsx
file. This is beneficial when the metadata is shared across multiple pages, such as in an eCommerce site where all product pages share common metadata.
// app/products/layout.tsx
export const metadata = {
title: "Products - My eCommerce Site",
description: "Browse our wide selection of products.",
};
export default function ProductsLayout({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
You can combine static, dynamic, and async metadata together. For example, you may want to define static metadata for common pages like the homepage but have dynamic metadata for product pages and async metadata for fetching product data.
export async function generateMetadata({
params,
}: {
params: { id: string };
}): Promise<Metadata> {
const staticMetadata = {
title: "Static Title",
description: "This is a static description",
};
const product = await fetchProductData(params.id); // Async fetching data
return {
...staticMetadata,
title: `${staticMetadata.title} | ${product.name}`,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
images: [
{
url: product.image,
},
],
},
};
}
This combination gives you flexibility to manage SEO metadata efficiently and effectively.
Next.js’s metadata API is a powerful tool to enhance your app’s SEO, social media sharing, and more. The combination of static, dynamic, and async metadata allows for great flexibility across different types of applications.
In Next.js, managing the page title and metadata is essential for SEO and a great user experience. Starting from Next.js version 13.3, it introduced the metadata
API, which allows for more flexibility and control over how metadata (like title, description, etc.) is defined for pages and components.
When working with the title
metadata, there are three important properties:
default
Property:absolute
Property:absolute: true
, the title will appear as is, without the default or template being applied.template
Property:"%s | MyWebsite"
would append the site name to each page title.Here’s how you can use the title
metadata in Next.js with default
, absolute
, and template
:
// app/layout.js (in Next.js app directory structure)
export const metadata = {
title: {
default: "My Website",
template: "%s | My Website",
absolute: false, // If true, it will ignore the template and default.
},
};
// For a specific page in app directory (e.g., app/about/page.js):
export const metadata = {
title: "About Us",
};
// Final output on this page would be "About Us | My Website"
default
:
template
:
%s
will be replaced with the actual page title.About Us
, the final title in the HTML will be: About Us | My Website
.absolute
:
true
, the title will be exactly what you specify on the page, without any default or template applied.absolute
is true
, the page with a title of About Us
will just have About Us
in the title tag, without the | My Website
suffix.<head>
:With Template:
<title>About Us | My Website</title>
With Absolute Title:
// app/contact/page.js
export const metadata = {
title: { absolute: true, default: "Contact Us" },
};
<title>Contact Us</title>