If you want your Next.js site to actually show up on Google (and look great when shared on social), this is what you should do β step by step.
π·οΈ Meta Tags β Why They Matter
Meta tags give search engines and social platforms info about your page.
If you skip them, your site might look like a blank page on Google or Twitter.
π Always add these basic meta tags:
π Open Graph meta tags: (for social sharing)
π Article-specific Open Graph tags:
(important for blog posts, articles, news)
π Twitter meta tags: (for Twitter/X previews)
βοΈ How to Add Meta Tags in Next.js
π In App Router, define viewport + metadata:
export const viewport = {
width: "device-width",
initialScale: 1,
themeColor: "#ffffff"
};
export const metadata = {
title: "Site Title",
description: "Short site description",
keywords: ["keyword1", "keyword2"],
openGraph: {
siteName: "My Site",
type: "website",
locale: "en_US",
images: [{ url: "https://yoursite.com/og-image.png", width: 1200, height: 630, alt: "My Site" }]
},
twitter: {
card: "summary_large_image",
title: "Site Title",
description: "Short site description",
images: [{ url: "https://yoursite.com/og-image.png", width: 1200, height: 630, alt: "My Site" }]
},
robots: {
index: true,
follow: true,
googleBot: "index, follow"
},
alternates: {
canonical: "https://yoursite.com"
}
};
π On dynamic pages (blog posts), use generateMetadata():
export async function generateMetadata({ params }) {
const post = await fetch(`YOUR_ENDPOINT/${params.slug}`).then(res => res.json());
return {
title: `${post.title} | My Site`,
description: post.description,
openGraph: {
title: `${post.title} | My Site`,
description: post.description,
type: "article",
url: `https://yoursite.com/${post.slug}`,
publishedTime: post.created_at,
modifiedTime: post.modified_at,
authors: ["https://yoursite.com/about"],
tags: post.categories,
images: [
{
url: `https://yoursite.com/assets/${post.slug}/thumbnail.png`,
width: 1024,
height: 576,
alt: post.title
}
]
},
twitter: {
card: "summary_large_image",
title: `${post.title} | My Site`,
description: post.description,
images: [{ url: `https://yoursite.com/assets/${post.slug}/thumbnail.png`, width: 1024, height: 576, alt: post.title }]
},
alternates: {
canonical: `https://yoursite.com/${post.slug}`
}
};
}
π JSON-LD Schema β Why Bother?
JSON-LD adds "structured data" for Google.
It helps Google understand your page type (Blog Post, Product, Event...) and improves rich snippets.
π Example for blog post:
const jsonLd = {
"@context": "https://schema.org",
"@type": "BlogPosting",
mainEntityOfPage: { "@type": "WebPage", "@id": "https://yoursite.com/my-post" },
headline: "Post Title",
description: "Post description",
image: "https://yoursite.com/assets/my-post/thumbnail.png",
datePublished: "2024-01-11T11:35:00+07:00",
dateModified: "2024-01-11T11:35:00+07:00",
author: { "@type": "Person", name: "Your Name", url: "https://linkedin.com/in/yourname" },
publisher: { "@type": "Person", name: "Your Name", logo: { "@type": "ImageObject", url: "https://yoursite.com/avatar.jpg" } },
inLanguage: "en-US",
isFamilyFriendly: "true"
};
π Render this in your component:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
π Use this tool to generate schemas: https://technicalseo.com/tools/schema-markup-generator/
πΊοΈ Sitemap β Why You Need It
Sitemap helps Google crawl all your pages. Without it, some deep links may never get indexed.
π In Pages Router:
Use next-sitemap
npm install next-sitemap
npx next-sitemapπ In App Router:
Manually define app/sitemap.ts:
export default async function sitemap() {
const pages = [
{ url: "https://yoursite.com", lastModified: new Date(), changeFrequency: "daily", priority: 1 },
{ url: "https://yoursite.com/about", lastModified: new Date(), changeFrequency: "monthly", priority: 0.9 }
// more pages
];
return pages;
}
π Result will be accessible at:
arduino
CopyEdit
https://yoursite.com/sitemap.xmlπ€ robots.txt β Controlling Crawlers
robots.txt tells search engines which pages to crawl or ignore.
π In Pages Router:
public/robots.txt:
makefile
CopyEdit
User-agent: *
Disallow:
Sitemap: https://yoursite.com/sitemap.xmlπ To block certain pages:
Disallow: /search?q=
Disallow: /admin
π In App Router:
Define app/robots.ts:
export default function robots() {
return {
rules: {
userAgent: "*",
allow: ["/"],
disallow: ["/search?q=", "/admin"]
},
sitemap: ["https://yoursite.com/sitemap.xml"]
};
}
π Important Link Tags
π Always include these in your head:
Summary β What You Should Do (Minimal Checklist)
β Add full Meta Tags (title, description, OG, Twitter)
β Add JSON-LD schema for key pages
β Generate and serve a sitemap.xml
β Serve a robots.txt
β Include proper link tags (canonical, icon, manifest)
If you do all of this, your Next.js site will:
β Look great in search results
β Show rich previews on social media
β Be fully crawlable and indexable by Google