Skip to content
DocsRoutingNavigation

Navigation APIs

💡

The navigation APIs are only needed when you’re using i18n routing.

next-intl provides lightweight wrappers around Next.js’ navigation APIs like <Link /> and useRouter that automatically handle the user locale and pathnames behind the scenes.

To create these APIs, you can call the createNavigation function with your routing configuration:

routing.ts
import {createNavigation} from 'next-intl/navigation';
import {defineRouting} from 'next-intl/routing';
 
export const routing = defineRouting(/* ... */);
 
export const {Link, redirect, usePathname, useRouter} =
  createNavigation(routing);

This function is typically called in a central module like src/i18n/routing.ts in order to provide easy access to navigation APIs in your components.

What if the locales aren’t known at build time?

In case you’re building an app where locales can be added and removed at runtime, createNavigation can be called without the locales argument, therefore allowing any string that is encountered at runtime to be a valid locale. In this case, you’d not use the defineRouting function.

routing.ts
import {createNavigation} from 'next-intl/navigation';
 
export const {Link, redirect, usePathname, useRouter} = createNavigation({
  // ... potentially other routing
  // config, but no `locales` ...
});

Note however that the locales argument for the middleware is still mandatory. If you need to fetch the available locales at runtime, you can provide the routing configuration for the middleware dynamically per request.

APIs

The created navigation APIs are thin wrappers around the equivalents from Next.js and mostly adhere to the same function signatures. Your routing configuration and the user’s locale are automatically incorporated.

If you’re using the pathnames setting in your routing configuration, the internal pathnames that are accepted for href arguments will be strictly typed and localized to the given locale.

How can I ensure consistent usage of navigation APIs?

To avoid importing APIs like <Link /> directly from Next.js by accident, you can consider linting for the consistent usage of internationalized navigation APIs.

This component wraps next/link and localizes the pathname as necessary.

import {Link} from '@/i18n/routing';
 
// When the user is on `/en`, the link will point to `/en/about`
<Link href="/about">About</Link>
 
// Search params can be added via `query`
<Link href={{pathname: "/users", query: {sortBy: 'name'}}}>Users</Link>
 
// You can override the `locale` to switch to another language
// (this will set the `hreflang` attribute on the anchor tag)
<Link href="/" locale="de">Switch to German</Link>

Depending on if you’re using the pathnames setting, dynamic params can either be passed as:

// 1. A final string (when not using `pathnames`)
<Link href="/users/12">Susan</Link>
 
// 2. An object (when using `pathnames`)
<Link href={{
  pathname: '/users/[userId]',
  params: {userId: '5'}
}}>
  Susan
</Link>

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 and localizes the pathname accordingly.

'use client';
 
import {useRouter} from '@/i18n/routing';
 
const router = useRouter();
 
// When the user is on `/en`, the router will navigate to `/en/about`
router.push('/about');
 
// Search params can be added via `query`
router.push({
  pathname: '/users',
  query: {sortBy: 'name'}
});
 
// You can override the `locale` to switch to another language
router.replace('/about', {locale: 'de'});

Depending on if you’re using the pathnames setting, dynamic params can either be passed as:

// 1. A final string (when not using `pathnames`)
router.push('/users/12');
 
// 2. An object (when using `pathnames`)
router.push({
  pathname: '/users/[userId]',
  params: {userId: '5'}
});
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 by navigating to the same pathname, while overriding the locale.

Depending on if you’re using the pathnames setting, you optionally have to forward params to potentially resolve an internal pathname.

'use client';
 
import {usePathname, useRouter} from '@/i18n/routing';
import {useParams} from 'next/navigation';
 
const pathname = usePathname();
const router = useRouter();
 
// Without `pathnames`: Pass the current `pathname`
router.replace(pathname, {locale: 'de'});
 
// With `pathnames`: Pass `params` as well
const params = useParams();
router.replace(
  // @ts-expect-error -- TypeScript will validate that only known `params`
  // are used in combination with a given `pathname`. Since the two will
  // always match for the current route, we can skip runtime checks.
  {pathname, params},
  {locale: 'de'}
);

usePathname

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

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

Note that if you’re using the pathnames setting, the returned pathname will correspond to an internal pathname template (dynamic params will not be replaced by their values).

// When the user is on `/de/ueber-uns`, this will be `/about`
const pathname = usePathname();
 
// When the user is on `/de/neuigkeiten/produktneuheit-94812`,
// this will be `/news/[articleSlug]-[articleId]`
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 and localizes the pathname as necessary.

Note that a locale prop is always required, even if you’re just passing the current locale.

import {redirect} from '@/i18n/routing';
 
// Redirects to `/en/login`
redirect({href: '/login', locale: 'en'});
 
// Search params can be added via `query`
redirect({href: '/users', query: {sortBy: 'name'}, locale: 'en'});

Depending on if you’re using the pathnames setting, dynamic params can either be passed as:

// 1. A final string (when not using `pathnames`)
redirect({href: '/users/12', locale: 'en'});
 
// 2. An object (when using `pathnames`)
redirect({
  href: {
    pathname: '/users/[userId]',
    params: {userId: '5'}
  },
  locale: 'en'
});
💡

permanentRedirect is supported too.

Why does TypeScript not narrow types correctly after calling redirect?

TypeScript currently has a limitation with control flow analysis, which results in not being able to narrow types correctly after calling redirect as well as detecting unreachable code:

import {routing} from '@/i18n/routing';
 
function UserProfile({userId}: {userId?: string}) {
  if (!userId) {
    redirect({href: '/login', locale: 'en'});
  }
 
  // `userId` should be narrowed to `string` here,
  // but TypeScript doesn't analyze this correctly
}

To work around this limitation, you can add an explicit type annotation to the redirect function:

routing.ts
const {redirect: _redirect} = createNavigation(routing);
 
// Enable type narrowing after calling `redirect`
export const redirect: typeof _redirect = _redirect;

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 for a page that accepts search params.

import {getPathname} from '@/i18n/routing';
 
// Will return `/en/about`
const pathname = getPathname({
  locale: 'en',
  href: '/about'
});
 
// Search params can be added via `query`
const pathname = getPathname({
  locale: 'en',
  href: {
    pathname: '/users',
    params: {sortBy: 'name'}
  }
});

Depending on if you’re using the pathnames setting, dynamic params can either be passed as:

// 1. A final string (when not using `pathnames`)
const pathname = getPathname({
  locale: 'en',
  href: '/users/12'
});
 
// 2. An object (when using `pathnames`)
const pathname = getPathname({
  locale: 'en',
  href: {
    pathname: '/users/[userId]',
    params: {userId: '5'}
  }
});

Legacy APIs

next-intl@3.0.0 brought the first release of the navigation APIs with these functions:

  • createSharedPathnamesNavigation
  • createLocalizedPathnamesNavigation

As part of next-intl@3.22.0, these functions have been replaced by a single createNavigation function, which unifies the API for both use cases and also fixes a few quirks in the previous APIs. Going forward, createNavigation is recommended and the previous functions are marked as deprecated.

While createNavigation is mostly API-compatible, there are some minor differences that should be noted. Please refer to the 3.22 announcement post for full details.

Docs

 · 

Examples

 · 

Blog