Linking between localized pages

Keeping users in their selected language

Problem

When language codes are part of the URL, creating internal links becomes complex. A simple link like <a href="/about"> is wrong, as it breaks the localized route, sending a user from /fr/contact to /about instead of /fr/about. This pulls the user out of their selected language.

Solution

Create a custom, wrapper component around the Next.js Link component. This new component will use the useParams hook to get the current language from the URL and automatically prepend it to any href it receives, ensuring all internal links are correctly localized.

Steps

Create a new file app/components/LocalizedLink.tsx. This must be a client component to use the useParams hook.

// app/components/LocalizedLink.tsx
'use client';

import Link from 'next/link';
import { useParams } from 'next/navigation';
import type { ComponentProps } from 'react';

type LinkProps = ComponentProps<typeof Link>;

export default function LocalizedLink({ href, ...rest }: LinkProps) {
  const params = useParams();
  const lang = params.lang as string;

  let localizedHref = href;

  // Check if href is a string and needs prefixing
  if (typeof href === 'string' && href.startsWith('/')) {
    localizedHref = `/${lang}${href}`;
  } else if (
    typeof href === 'object' &&
    href !== null &&
    href.pathname?.startsWith('/')
  ) {
    // Re-create the object with a prefixed pathname
    localizedHref = {
      ...href,
      pathname: `/${lang}${href.pathname}`,
    };
  }
  // Absolute URLs or other cases are passed through

  return <Link href={localizedHref} {...rest} />;
}

This component imports the standard Link props. It checks if href is a string (like /about) or an object (like { pathname: '/about' }) and intelligently prepends the current lang (e.g., es) to it.

2. Use the component on your pages

Now, in your pages, import LocalizedLink instead of the standard next/link. You can use it just as you would the normal Link component, but without worrying about the language prefix.

// app/[lang]/page.tsx
import LocalizedLink from '@/app/components/LocalizedLink';

export default function Home({ params }: { params: { lang: string } }) {
  return (
    <div>
      <h1>Home page</h1>
      <p>Current language: {params.lang}</p>
      
      <nav>
        <ul>
          <li>
            {/* This will render as /en/about or /es/about, etc. */}
            <LocalizedLink href="/about">About page</LocalizedLink>
          </li>
          <li>
            {/* This also works */}
            <LocalizedLink href={{ pathname: '/contact' }}>
              Contact page
            </LocalizedLink>
          </li>
        </ul>
      </nav>
    </div>
  );
}

Using <LocalizedLink href="/about"> now correctly renders a link to /{current_lang}/about, keeping the user within their selected language as they navigate your site.