Skip to content
next-intl 3.0 is out! (announcement)
Docs
Routing
Navigation

Next.js App Router internationalized navigation

next-intl provides drop-in replacements for common Next.js navigation APIs that automatically handle the user locale behind the scenes.

Strategies

There are two strategies that you can use based on your needs.

Shared pathnames: The simplest case is when your app uses the same pathnames, regardless of the locale.

For example:

  • /en/about
  • /de/about

Localized pathnames: Many apps choose to localize pathnames, especially when search engine optimization is relevant. In this case, you'll provide distinct pathnames based on the user locale.

For example:

  • /en/about
  • /de/ueber-uns

Note: The terms "shared" and "localized" pathnames are used to refer to pathnames that are created via the file-system based routing in Next.js. If you're using an external system like a CMS to localize pathnames, you'll typically implement this with a catch-all route like [locale]/[[...slug]].


Each strategy will provide you with corresponding navigation APIs that you'll typically provide in a central module to easily access them in components (e.g. src/navigation.ts).

Strategy 1: Shared pathnames

With this strategy, the pathnames of your app are identical for all locales. This is the simplest case, because the routes you define in Next.js will map directly to the pathnames that a user can request.

To create navigation APIs for this strategy, use the createSharedPathnamesNavigation function:

navigation.ts
import {createSharedPathnamesNavigation} from 'next-intl/navigation';
 
export const locales = ['en', 'de'] as const;
 
export const {Link, redirect, usePathname, useRouter} =
  createSharedPathnamesNavigation({locales});

The locales argument is identical to the configuration that you pass to the middleware. To reuse it there, you can import the locales into the middleware.

middleware.ts
import createMiddleware from 'next-intl/middleware';
import {locales} from './navigation';
 
export default createMiddleware({
  defaultLocale: 'en',
  locales
});

Strategy 2: Localized pathnames

When using this strategy, you have to provide distinct pathnames for every locale that your app supports. However, the localized variants will be handled by a single route internally, therefore a mapping needs to be provided that is also consumed by the middleware.

You can use the createLocalizedPathnamesNavigation function to create corresponding navigation APIs:

navigation.ts
import {
  createLocalizedPathnamesNavigation,
  Pathnames
} from 'next-intl/navigation';
 
export const locales = ['en', 'de'] as const;
 
// The `pathnames` object holds pairs of internal
// and external paths, separated by locale.
export const pathnames = {
  // If all locales use the same pathname, a
  // single external path can be provided.
  '/': '/',
  '/blog': '/blog',
 
  // If locales use different paths, you can
  // specify each external path per locale.
  '/about': {
    en: '/about',
    de: '/ueber-uns'
  },
 
  // Dynamic params are supported via square brackets
  '/news/[articleSlug]-[articleId]': {
    en: '/news/[articleSlug]-[articleId]',
    de: '/neuigkeiten/[articleSlug]-[articleId]'
  },
 
  // Also (optional) catch-all segments are supported
  '/categories/[...slug]': {
    en: '/categories/[...slug]',
    de: '/kategorien/[...slug]'
  }
} satisfies Pathnames<typeof locales>;
 
export const {Link, redirect, usePathname, useRouter, getPathname} =
  createLocalizedPathnamesNavigation({locales, pathnames});

The pathnames argument is identical to the configuration that you pass to the middleware for localizing pathnames. Because of this, you might want to import the locales and pathnames into the middleware.

middleware.ts
import createMiddleware from 'next-intl/middleware';
import {locales, pathnames} from './navigation';
 
export default createMiddleware({
  defaultLocale: 'en',
  locales,
  pathnames
});
💡

Have a look at the App Router example to explore a working implementation of localized pathnames.

APIs

Link

This component wraps next/link (opens in a new tab) and automatically prefixes the href with the current locale as necessary. If the default locale is matched, the href remains unchanged and no prefix is added.

import {Link} from '../navigation';
 
// When the user is on `/en`, the link will point to `/en/about`
<Link href="/about">About</Link>
 
// You can override the `locale` to switch to another language
<Link href="/" locale="de">Switch to German</Link>
 
// Dynamic params need to be interpolated into the pathname
<Link href="/users/12">Susan</Link>
How can I render a navigation link?

The useSelectedLayoutSegment hook (opens in a new tab) from Next.js allows you to detect if a given child segment is active from within the parent layout. Since this returns an internal pathname, it can be matched against an href that you can pass to Link.

NavigationLink.tsx
'use client';
 
import {useSelectedLayoutSegment} from 'next/navigation';
import {ComponentProps} from 'react';
import {Link} from '../navigation';
 
export default function NavigationLink({
  href,
  ...rest
}: ComponentProps<typeof Link>) {
  const selectedLayoutSegment = useSelectedLayoutSegment();
  const pathname = selectedLayoutSegment ? `/${selectedLayoutSegment}` : '/';
  const isActive = pathname === href;
 
  return (
    <Link
      aria-current={isActive ? 'page' : undefined}
      href={href}
      style={{fontWeight: isActive ? 'bold' : 'normal'}}
      {...rest}
    />
  );
}
Navigation.tsx
<nav>
  <NavigationLink href="/">{t('home')}</NavigationLink>
  <NavigationLink href="/about">{t('about')}</NavigationLink>
  <NavigationLink href="/blog">{t('blog')}</NavigationLink>
</nav>

See also the Next.js docs on creating an active link component (opens in a new tab).

How does prefetching of localized links work?

Just like next/link, by default all links are prefetched. The one exception to this is that links to other locales aren't prefetched, because this would result in prematurely overwriting the locale cookie.

useRouter

If you need to navigate programmatically, e.g. in an event handler, next-intl provides a convience API that wraps useRouter from Next.js (opens in a new tab) and automatically applies the locale of the user.

'use client';
 
import {useRouter} from '../navigation';
 
const router = useRouter();
 
// When the user is on `/en`, the router will navigate to `/en/about`
router.push('/about');
 
// You can override the `locale` to switch to another language
router.replace('/about', {locale: 'de'});
 
// Dynamic params need to be interpolated into the pathname
router.push('/users/12', {locale: 'de'});
 
How can I change the locale for the current page?

By combining usePathname with useRouter, you can change the locale for the current page programmatically.

'use client';
 
import {usePathname, useRouter} from '../navigation';
 
const pathname = usePathname();
const router = useRouter();
 
router.replace(pathname, {locale: 'de'});

usePathname

To retrieve the pathname without a potential locale prefix, you can call usePathname.

'use client';
 
import {usePathname} from '../navigation';
 
// When the user is on `/en`, this will be `/`
const pathname = usePathname();

redirect

If you want to interrupt the render and redirect to another page, you can invoke the redirect function. This wraps the redirect function from Next.js (opens in a new tab) and automatically applies the current locale.

import {redirect} from '../navigation';
 
// When the user is on `/en`, this will be `/en/login`
redirect('/login');
 
// Dynamic params need to be interpolated into the pathname
router.push('/users/12');

getPathname

If you need to construct a particular pathname based on a locale, you can call the getPathname function. This can for example be useful to retrieve a canonical link (opens in a new tab) for a page that accepts search params.

(This API is only available for localized pathnames, since it is not necessary for shared pathnames.)