- seo (Search Engine Optimization) is one of the key factors to rank your website higher on Google and receive more traffic.
I've been on a deep dive into Next.js SEO, and I'm excited to share what I've learned. This tutorial provides a clear, actionable checklist to help you optimize your blog and see real results in search engine rankings. Let's make your content shine!
1. Meta Tags
Ensure your website's SEO and social media presence are strong by implementing this meta tag checklist:
- Standard Meta Tags : title, description, keywords, robots, viewport, charSet
- Open Graph Meta Tags : og:site_name, og:local, og:title, og:description, og:type, og:url, og:image, og:image:alt, og:image:type, og:image:width, og:image:height
- Article Meta Tags (Open Graph): article:published_time, article:modified_time, article:author
- Twitter Meta Tags: twitter:card, twitter:site, twitter:creator, twitter:title, twitter:description, twitter:image
For optimal compatibility with social media platforms, utilize PNG or JPG image formats in the
og:image
andtwitter:image
meta tags, as WebP support is inconsistent.
Meta Tags for Next.js App Router
The App Router provides export const metadata
for static metadata and generateMetadata
for dynamic metadata generation, simplifying meta tag implementation in Next.js.
Want to know how? The metadata docs have all the info.
Static Metadata
You can define static metadata by adding the export const metadata
argument inside page.tsx
or layout.tsx
:
import type { Viewport, Metadata } from "next";
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
themeColor: "#ffffff"
};
export const metadata: Metadata = {
title: "This is meta title",
description: "This is meta description.",
metadataBase: new URL("https://example.com"),
keywords: [
"example",
"seo",
"javascript",
"nextjs",
],
robots: {
index: true,
follow: true,
"max-image-preview": "large",
"max-snippet": -1,
"max-video-preview": -1,
googleBot: "index, follow"
},
alternates: {
canonical: "https://example.com",
types: {
"application/rss+xml": "https://example.com/rss.xml"
}
},
applicationName: "Example | Chandu Bobbili",
appleWebApp: {
title: "Example | Chandu Bobbili",
statusBarStyle: "default",
capable: true
},
verification: {
google: "YOUR_DATA",
yandex: ["YOUR_DATA"],
other: {
"msvalidate.01": ["YOUR_DATA"],
"facebook-domain-verification": ["YOUR_DATA"]
}
},
icons: {
icon: [
{
url: "/favicon.ico",
type: "image/x-icon"
},
{
url: "/favicon-16x16.png",
sizes: "16x16",
type: "image/png"
}
// add favicon-32x32.png, favicon-96x96.png, android-chrome-192x192.png
],
shortcut: [
{
url: "/favicon.ico",
type: "image/x-icon"
}
],
apple: [
{
url: "/apple-icon-57x57.png",
sizes: "57x57",
type: "image/png"
},
{
url: "/apple-icon-60x60.png",
sizes: "60x60",
type: "image/png"
}
// add apple-icon-72x72.png, apple-icon-76x76.png, apple-icon-114x114.png, apple-icon-120x120.png, apple-icon-144x144.png, apple-icon-152x152.png, apple-icon-180x180.png
]
},
openGraph: {
url: "https://example.com",
type: "website",
title: "This is title",
description: "This is the description",
images: [
{
url: "https://example.com/images/home/thumbnail.png",
width: 1200,
height: 630,
alt: "image"
}
]
},
twitter: {
card: "summary_large_image",
title: "This is title",
description: "This is the description",
creator: "@ChanduBobbili",
site: "@ChanduBobbili",
images: [
{
url: "https://example.com/images/home/thumbnail.png",
width: 1200,
height: 630,
alt: "dminhvu"
}
]
},
};
Dynamic Metadata
Implement the generateMetadata
function to define dynamic metadata for pages with dynamic route segments, such as [slug]/page.tsx
or [id]/page.tsx
.
import type { Metadata, ResolvingMetadata } from "next";
type Params = {
slug: string;
};
type Props = {
params: Params;
searchParams: { [key: string]: string | string[] | undefined };
};
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const { slug } = params;
const post: Post = await fetch(`YOUR_ENDPOINT`, {
method: "GET",
next: {
revalidate: 60 * 60 * 24
}
}).then((res) => res.json());
return {
title: `${post.title} | Webpage`,
authors: [
{
name: post.author || "Chandu"
}
],
description: post.description,
keywords: post.keywords,
openGraph: {
title: `${post.title} | Webpage`,
description: post.description,
type: "article",
url: `https://example.com/${post.slug}`,
publishedTime: post.created_at,
modifiedTime: post.modified_at,
authors: ["https://example.com/about"],
tags: post.categories,
images: [
{
url: `https://example/assets/${post.slug}/thumbnail.png?tr=f-png`,
width: 1024,
height: 576,
alt: post.title,
type: "image/png"
}
]
},
twitter: {
card: "summary_large_image",
site: "@ChanduBobbili",
creator: "@ChanduBobbili",
title: `${post.title} | webpage`,
description: post.description,
images: [
{
url: `https://example/assets/${post.slug}/thumbnail.png?tr=f-png`,
width: 1024,
height: 576,
alt: post.title
}
]
},
alternates: {
canonical: `https://example.com/${post.slug}`
}
};
}
Next.js App Router automatically includes charSet and viewport, so you don't need to define them manually.
Meta Tags for Next.js Pages Router
You'll find these meta tags present on this page when using the Next.js Pages Router:
import Head from "next/head";
export default function Page() {
return (
<Head>
<title>Example | Webpage</title>
<meta
name="description"
content="Learn how to optimize your Next.js website for SEO by following this complete checklist."
/>
<meta
name="keywords"
content="nextjs seo complete checklist, nextjs seo tutorial"
/>
<meta name="robots" content="index, follow" />
<meta name="googlebot" content="index, follow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charSet="utf-8" />
<meta property="og:site_name" content="Blog | Webpage" />
<meta property="og:locale" content="en_US" />
<meta
property="og:title"
content="Enter your title here"
/>
<meta
property="og:description"
content="Enter your description here"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://example.com/nextjs-seo" />
<meta
property="og:image"
content="https://example/assets/nextjs-seo/thumbnail.png?tr=f-png"
/>
<meta property="og:image:alt" content="Next.js SEO" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="article:published_time"
content="2024-01-11T11:35:00+07:00"
/>
<meta
property="article:modified_time"
content="2024-01-11T11:35:00+07:00"
/>
<meta
property="article:author"
content="https://www.linkedin.com/in/chandu-bobbili-15863319b"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@ChanduBobbili" />
<meta name="twitter:creator" content="@ChanduBobbili" />
<meta
name="twitter:title"
content="Enter your title here"
/>
<meta
name="twitter:description"
content="Enter your description here"
/>
<meta
name="twitter:image"
content="https://example/assets/nextjs-seo/thumbnail.png?tr=f-png"
/>
</Head>
);
}
2. Sitemap
A sitemap helps search engines find and index your website's content, which is crucial for SEO.
Sitemap for Next.js App Router
For Next.js App Router, you can define the sitemap.ts
file at app/sitemap.ts
:
import {
getAllCategories,
getAllPostSlugsWithModifyTime
} from "@/utils/getData";
import { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const defaultPages = [
{
url: "https://example.com",
lastModified: new Date(),
changeFrequency: "daily",
priority: 1
},
{
url: "https://example.com/about",
lastModified: new Date(),
changeFrequency: "monthly",
priority: 0.9
},
{
url: "https://example.com/contact",
lastModified: new Date(),
changeFrequency: "monthly",
priority: 0.9
}
// other pages
];
const postSlugs = await getAllPostSlugsWithModifyTime();
const categorySlugs = await getAllCategories();
const sitemap = [
...defaultPages,
...postSlugs.map((e: any) => ({
url: `https://example.com/${e.slug}`,
lastModified: e.modified_at,
changeFrequency: "daily",
priority: 0.8
})),
...categorySlugs.map((e: any) => ({
url: `https://example.com/category/${e}`,
lastModified: new Date(),
changeFrequency: "daily",
priority: 0.7
}))
];
return sitemap;
}
With this sitemap.ts
file created, you can access the sitemap at https://example.com/sitemap.xml
.
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com</loc>
<lastmod>2024-01-11T02:03:09.613Z</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<!-- other pages -->
</urlset>
Visit Next.js App Router Sitemap to learn more.
Sitemap for Next.js Page Router
If you're using the Pages Router in Next.js, next-sitemap is a convenient tool for sitemap creation following your build.
For example, running the following command will install next-sitemap
and generate a sitemap for your app:
npm install next-sitemap
npx next-sitemap
next-sitemap
requires a basic config file(next-sitemap.config.js)
under your project root, checkout
A sitemap will be generated at public/sitemap.xml
:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://example.com</loc>
<lastmod>2024-01-11T02:03:09.613Z</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<!-- other pages -->
</urlset>
3. robots.txt
A robots.txt
file guides search engine crawlers by defining which pages they are allowed or disallowed to access.
robots.txt for Next.js App Router
For Next.js App Router, you don't need to manually define a robots.txt
file. Instead, you can define the robots.ts
file at app/robots.ts
:
import { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: ["/"],
disallow: ["/search?q=", "/admin/"]
},
sitemap: ["https://example.com/sitemap.xml"]
};
}
With this robots.ts
file created, you can access the robots.txt
file at https://example.com/robots.txt
.
robots.txt for Next.js Pages Router
For Next.js Pages Router, you can create a robots.txt
file at public/robots.txt
:
User-agent: *
Disallow:
Sitemap: https://example.com/sitemap.xml
For pages you don't want indexed, such as search results or those marked 'noindex', this line will block search engine crawlers:
User-agent: *
Disallow: /search?q=
Disallow: /admin
4. Link Tags
Beyond meta
tags, your website requires the inclusion of several key link
tags:
- canonical
- alternative
- icon
- apple-touch-icon
- manifest
Link Tags for Next.js App Router
Link tags in Next.js App Router are handled in the same way as meta tags, via export const metadata
or generateMetadata
.
import type { Viewport, Metadata } from "next";
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
themeColor: "#ffffff"
};
export const metadata: Metadata = {
// other parts
alternates: {
canonical: "https://example.com",
types: {
"application/rss+xml": "https://example.com/rss.xml"
}
},
icons: {
icon: [
{
url: "/favicon.ico",
type: "image/x-icon"
},
{
url: "/favicon-16x16.png",
sizes: "16x16",
type: "image/png"
}
// add favicon-32x32.png, favicon-96x96.png, android-chrome-192x192.png
],
shortcut: [
{
url: "/favicon.ico",
type: "image/x-icon"
}
],
apple: [
{
url: "/apple-icon-57x57.png",
sizes: "57x57",
type: "image/png"
},
{
url: "/apple-icon-60x60.png",
sizes: "60x60",
type: "image/png"
}
// add apple-icon-72x72.png, apple-icon-76x76.png, apple-icon-114x114.png, apple-icon-120x120.png, apple-icon-144x144.png, apple-icon-152x152.png, apple-icon-180x180.png
]
},
};
Link Tags for Next.js Pages Router
To demonstrate the link
tags, this page would contain the following when using the Pages Router
:
import Head from "next/head";
export default function Page() {
return (
<Head>
{/* other parts */}
<link
rel="alternate"
type="application/rss+xml"
href="https://example.com/rss.xml"
/>
<link rel="canonical" href="https://example.com/nextjs-seo" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png" />
{/* add apple-touch-icon-72x72.png, apple-touch-icon-76x76.png, apple-touch-icon-114x114.png, apple-touch-icon-120x120.png, apple-touch-icon-144x144.png, apple-touch-icon-152x152.png, apple-touch-icon-180x180.png */}
<link
rel="icon"
type="image/png"
href="/favicon-16x16.png"
sizes="16x16"
/>
{/* add favicon-32x32.png, favicon-96x96.png, android-chrome-192x192.png */}
</Head>
);
}
5. JSON-LD Schema
JSON-LD, a lightweight Linked Data format, facilitates machine parsing and generation, contributing to its widespread adoption.
This part can be applied to both Pages Router and App Router.
You can easily generate JSON-LD Schema for your website by using the Schema Markup Generator Tool.
And I can put it anywhere, whether inside the head
tag or the body
tag is fine:
import Head from "next/head";
export default function Page() {
return (
<div>
{/* other parts */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
</div>
);
}
6. Script Optimization
Script Optimization for General Scripts
Next.js provides a built-in component called <Script>
to add external scripts to your website.
For example, you can add Google Analytics to your website by adding the following script tag:
import Head from "next/head";
import Script from "next/script";
export default function Page() {
return (
<Head>
{/* other parts */}
{process.env.NODE_ENV === "production" && (
<>
<Script async strategy="afterInteractive" id="analytics">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
`}
</Script>
</>
)}
</Head>
);
}
Script Optimization for Common Third-Party Integrations
Next.js App Router introduces a new library called @next/third-parties for:
- Google Tag Manager
- Google Analytics
- Google Maps Embed
- Youtube Embed
To use the
@next/third-parties
library, you need to install it:
npm install @next/third-parties
import { GoogleTagManager } from "@next/third-parties/google";
import { GoogleAnalytics } from "@next/third-parties/google";
import Head from "next/head";
export default function Page() {
return (
<html lang="en" className="scroll-smooth" suppressHydrationWarning>
{process.env.NODE_ENV === "production" && (
<>
<GoogleAnalytics gaId="G-XXXXXXXXXX" />
{/* other scripts */}
</>
)}
{/* other parts */}
</html>
);
}
7. Image Optimization
This part can be applied to both Pages Router and App Router.
Image optimization is also an important part of SEO as it helps your website load faster.
Faster image rendering directly influences your Google PageSpeed score, which in turn benefits user experience and SEO.
You can use next/image to optimize images in your Next.js website.
import Image from "next/image";
export default function Page() {
return (
<Image
src="https://example/assets/nextjs-seo/thumbnail.png?tr=f-webp"
alt="Next.js SEO"
width={1200}
height={630}
/>
);
}
For the image format, I prefer to use WebP because it has a smaller size than PNG and JPEG.
Conclusion
Here's a conclusion summarizing the key areas for website performance and SEO optimization in Next.js:
- Meta Tags: Implement comprehensive meta tags for improved search engine understanding and display.
- JSON-LD Schema: Utilize JSON-LD to provide structured data, enhancing search engine comprehension and rich results.
- Sitemap: Generate and submit a sitemap to ensure search engines can effectively crawl and index your website.
- robots.txt: Use robots.txt to control search engine crawler access, specifying which pages to crawl or ignore.
-
Link Tags: Implement
<link>
tags for canonical URLs, icons, and other essential link-related information. -
Script Optimization:
- Leverage Next.js's
<Script>
component for efficient script loading. - Utilize
@next/third-parties
for optimized handling of common third-party scripts.
- Leverage Next.js's
-
Image Optimization:
- Use Next.js's
<Image>
component for optimized image loading and performance. - Implement a CDN to cache images, improving loading speeds.
- Adopt modern image formats like WebP for reduced file sizes and faster delivery.
- Use Next.js's
Top comments (0)