Most Next.js sites I audit look great and load fast, but Google still treats them badly. Pages sit in "Discovered, currently not indexed" for weeks, the wrong URL shows up in search, or the title Google displays is not the one you wrote.

Almost all of it comes down to a handful of things you set once in the App Router. Here is the checklist I run on every Next.js project before launch.

1. Set metadataBase and a title template

Without metadataBase, relative URLs in canonical tags and Open Graph images can break. Set it once in the root layout, along with a title template so every page gets a consistent, unique title.

// app/layout.tsx import type { Metadata } from "next"; export const metadata: Metadata = { metadataBase: new URL("https://www.example.com"), title: { default: "Example Studio | Web Development", template: "%s | Example Studio", }, description: "Fast websites and web apps that rank on Google.", }; 

2. Give every page its own canonical URL

Pick one version of your domain (www or non-www, with or without a trailing slash) and make every page declare it. Duplicate URLs split your ranking signals.

// app/services/page.tsx export const metadata = { title: "Services", description: "Website development, SaaS and technical SEO.", alternates: { canonical: "/services/" }, }; 

Then make sure the other versions 301 redirect to it. If your canonical uses a trailing slash, set trailingSlash: true in next.config.js so internal links match.

3. Generate a sitemap that includes every page you care about

A page that is not in your sitemap and has no internal links pointing to it is invisible. I recently saw a site where the main /services/ page was "unknown to Google" for exactly this reason.

// app/sitemap.ts import type { MetadataRoute } from "next"; export default function sitemap(): MetadataRoute.Sitemap { const base = "https://www.example.com"; const pages = ["", "/services/", "/work/", "/blog/", "/contact/"]; return pages.map((path) => ({ url: `${base}${path}`, lastModified: new Date(), })); } 

For blog posts or case studies, pull the slugs from your CMS or database here instead of hard-coding them. Submit /sitemap.xml in Google Search Console.

4. Add robots.ts and point it at the sitemap

// app/robots.ts import type { MetadataRoute } from "next"; export default function robots(): MetadataRoute.Robots { return { rules: { userAgent: "*", allow: "/" }, sitemap: "https://www.example.com/sitemap.xml", }; } 

Check that staging or preview deployments are not accidentally served with noindex, and that production is not accidentally blocked.

5. Add structured data with JSON-LD

Schema markup helps Google, and AI search tools, understand who you are and what a page is about. For a local business or freelancer, ProfessionalService or LocalBusiness is a good start.

const jsonLd = { "@context": "https://schema.org", "@type": "ProfessionalService", name: "Example Studio", url: "https://www.example.com", telephone: "+1-555-0100", areaServed: ["City A", "City B"], }; export default function Page() { return ( <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> ); } 

Keep the name, phone number and URL exactly the same as on your Google Business Profile and directory listings.

6. Protect Core Web Vitals

  • Use next/image with explicit width and height, and add priority to the hero image so LCP is not delayed.
  • Load fonts with next/font to avoid layout shift and extra network requests.
  • Keep heavy client components out of the first screen. Server components by default, "use client" only where you need interactivity.
  • Test on a real mid-range Android phone on mobile data, not just on your laptop.

7. Fix "Discovered, currently not indexed"

This status means Google knows the URL but has not crawled it yet. Usually it is a priority problem, not a technical one. What helps:

  • Link to the page from your main navigation or from pages that are already indexed.
  • Make sure the page has real, unique content, not a thin template with the city name swapped.
  • Request indexing in Search Console URL Inspection once the above is fixed.

"Crawled, currently not indexed" is different: Google fetched the page and decided it was not worth indexing. That is a content quality signal, so improve the page rather than resubmitting it.

8. Check it all in Search Console

After launch, inspect a few key URLs in Search Console. Confirm the Google-selected canonical matches yours, the page is mobile friendly, and the structured data is detected. Then watch the Pages report weekly for the first month.

I'm Ujjwal Rupakheti, a full-stack developer and SEO expert in Nepal. I build Next.js sites and SaaS products and run this checklist on every one. If you have questions about indexing or technical SEO on your own Next.js site, ask in the comments.