How to Implement hreflang in Next.js

How to set up language URLs and hreflang in the Next.js App Router with the Metadata API or sitemap, avoid auto-redirects, and fix the most common hreflang mistakes.

To implement hreflang in Next.js, put the language in the URL path (for example /en/ and /hi/), render each language version on the server, and declare every version, including an x-default, through the alternates.languages field of the Metadata API or in the sitemap. Every version must list all the others and itself, and the codes must be valid ISO language and region codes. Do not redirect visitors automatically by browser language, because Google’s crawler usually does not send a language preference and may never see your other versions.

What hreflang does

hreflang tells Google which URL to show to users of a given language or region when you have equivalent pages for several. It does not merge rankings or translate anything. Google supports three ways to declare it: link elements in the HTML head, HTTP headers, or the XML sitemap. Use one method consistently.

Step 1: language in the path

app/
  [lang]/
    layout.js
    page.js
    services/[slug]/page.js
// app/[lang]/layout.js
export const locales = ["en", "hi"];

export async function generateStaticParams() {
  return locales.map((lang) => ({ lang }));
}

export default async function LangLayout({ children, params }) {
  const { lang } = await params;
  return <html lang={lang}><body>{children}</body></html>;
}

Path-based URLs are clear to users and crawlers. Avoid language chosen only by cookie or browser setting on a single URL, which gives Google one version to see.

Step 2: declare alternates in metadata

// app/[lang]/services/[slug]/page.js
const site = "https://www.example.com";

export async function generateMetadata({ params }) {
  const { lang, slug } = await params;
  return {
    alternates: {
      canonical: `${site}/${lang}/services/${slug}/`,
      languages: {
        "en-IN": `${site}/en/services/${slug}/`,
        "hi-IN": `${site}/hi/services/${slug}/`,
        "x-default": `${site}/en/services/${slug}/`,
      },
    },
  };
}

Next.js outputs a link rel=”alternate” hreflang element for each entry. The canonical points to the page itself; each language version is canonical for its own language. Never canonicalise translations to the English page, which tells Google to ignore them.

Step 3: or declare them in the sitemap

For large sites, the sitemap is easier to keep complete. Next.js sitemaps accept alternates:

// app/sitemap.js
export default async function sitemap() {
  const services = await getServices();
  const locales = ["en", "hi"];
  return services.flatMap((s) =>
    locales.map((lang) => ({
      url: `https://www.example.com/${lang}/services/${s.slug}/`,
      lastModified: s.updatedAt,
      alternates: {
        languages: Object.fromEntries(
          locales.map((l) => [l, `https://www.example.com/${l}/services/${s.slug}/`])
        ),
      },
    }))
  );
}

Each language version gets its own entry, and each entry lists every version, including itself.

Step 4: do not auto-redirect crawlers

Redirecting by Accept-Language or IP in middleware is the most common hreflang failure. Googlebot mostly crawls from the United States and usually without a language header, so it may be sent to one version every time. Google recommends letting users switch languages with visible links, and if you suggest a language, use a banner instead of a forced redirect. Keep the root URL either as a language selector or as the x-default version.

Common mistakes

Mistake Correct
en-UK en-GB (UK is not the ISO region code)
Missing return links: English lists Hindi, Hindi does not list English Every version lists all versions, including itself
hreflang pointing to redirected or noindexed URLs Point only to live, indexable, canonical URLs
Translations canonicalised to English Each version self-canonical
Machine-translated pages with no review Reviewed translations with localised prices, contact details and examples

Test

  1. View the source of each version and check the alternate links and canonical.
  2. curl each URL with no Accept-Language header and confirm no redirect happens.
  3. In Search Console, use URL Inspection on each version to confirm Google chose the page itself as canonical.

Frequently asked questions

Do I need hreflang for Hindi and English versions of an Indian site?

Yes, if the pages are equivalent translations. It helps Google show Hindi pages to Hindi searchers and English pages to others.

Does hreflang affect AI assistants?

AI assistants do not document using hreflang. Clear language paths and a correct html lang attribute help any system understand which language a page is in.

Should regional English versions exist, such as en-US and en-IN?

Only if content genuinely differs: prices, currency, contact details or examples. Identical pages with different codes add maintenance without benefit.

Sources

  1. Google Search Central, “Tell Google about localized versions of your page”
  2. Google Search Central, “Managing multi-regional and multilingual sites”
  3. Next.js documentation, Metadata API alternates and sitemap alternates