How to link alternative language versions in Next.js (Pages Router) v16
Link language alternatives for search engines
Problem
When a website serves the same content in multiple languages, search engines encounter separate URLs for each version without understanding their relationship. A French user searching might see the English version ranked higher than the French one, even though both exist. Similarly, an English page and its French translation might be treated as competing duplicates rather than coordinated alternatives. Without explicit signals connecting these language versions, search engines cannot confidently serve the most appropriate version to users based on their language preferences, leading to fragmented ranking authority and poor user experience.
Solution
Add link elements with hreflang attributes to the head section of each page, listing all language versions including the page itself. Each page variant must include an identical set of links referencing every available language version. This bidirectional linking ensures search engines recognize the pages as translations rather than duplicates, allowing them to serve the correct language version to users based on browser preferences and search context.
Steps
1. Create a component to generate alternate language links
Import the Head component from next/head to modify page metadata. Access locale information via the useRouter hook to build links for all available language versions.
import Head from "next/head";
import { useRouter } from "next/router";
interface AlternateLinksProps {
path?: string;
}
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, locale: currentLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
The component maps over available locales from the router and generates link elements dynamically for all pages.
2. Add the component to pages that have translations
Include the AlternateLinks component in each page component that exists in multiple languages.
import AlternateLinks from "@/components/AlternateLinks";
export default function AboutPage() {
return (
<>
<AlternateLinks />
<main>
<h1>About Us</h1>
</main>
</>
);
}
The component ensures each page includes the complete set of alternate links, satisfying the requirement that all variants reference each other.
3. Include a self-referencing link for the current locale
Every page must include a self-referencing hreflang tag indicating its own language. The component already handles this by iterating through all locales including the current one.
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
Each page now includes hreflang tags pointing to itself alongside tags for other versions.
4. Add an x-default fallback link
Add an x-default link to specify which version users should see when their language is not available.
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, defaultLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales || !defaultLocale) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
<link
rel="alternate"
hrefLang="x-default"
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${defaultLocale}${canonicalPath}`}
/>
</Head>
);
}
The x-default link directs users whose language preferences are not matched to the default locale.