Skip to content
3.0 preview (see announcement)
Docs
Environments
Error files (e.g. not-found)

Internationalization in Next.js error files

The Next.js App Router's file convention provides two files that can be used for error handling:

  1. not-found.js (opens in a new tab)
  2. error.js (opens in a new tab)

This page provides practical guides for these cases.

💡

Have a look at the App Router example to explore a working app with error handling.

not-found.js

Next.js renders the closest not-found page when a route segment calls the notFound function (opens in a new tab). We can use this mechanism to provide a localized 404 page by adding a not-found file within the [locale] folder.

app/[locale]/not-found.tsx
import {useTranslations} from 'next-intl';
 
export default function NotFoundPage() {
  const t = useTranslations('NotFoundPage');
  return <h1>{t('title')}</h1>;
}

Note however that Next.js will only render this page when the notFound function is called from within a route, not for all unknown routes in general.

Catching unknown routes

To catch unknown routes too, you can define a catch-all route that explicitly calls the notFound function.

app/[locale]/[...rest]/page.tsx
import {notFound} from 'next/navigation';
 
export default function CatchAllPage() {
  notFound();
}

After this change, all requests that are matched within the [locale] segment will render the not-found page when an unknown route is encountered.

Catching non-localized requests

When the user requests a route that is not matched by the next-intl middleware, there's no locale associated with the request (depending on your matcher config, e.g. /unknown.txt might not be matched).

You can add a root not-found page to handle these cases too.

app/not-found.tsx
'use client';
 
import Error from 'next/error';
 
// Render the default Next.js 404 page when a route
// is requested that doesn't match the middleware and
// therefore doesn't have a locale associated with it.
 
export default function NotFound() {
  return (
    <html lang="en">
      <body>
        <Error statusCode={404} />
      </body>
    </html>
  );
}

Note that the presence of app/not-found.tsx requires that a root layout is available, even if it's just passing children through.

app/layout.tsx
// Since we have a root `not-found.tsx` page, a layout file
// is required, even if it's just passing children through.
export default function RootLayout({children}) {
  return children;
}

For the 404 page to render, we need to call the notFound function within [locale]/layout.tsx when we detect an incoming locale param that isn't a valid locale.

app/[locale]/layout.tsx
import {useLocale} from 'next-intl';
import {notFound} from 'next/navigation';
 
const locales = ['en', 'de'];
 
export default function LocaleLayout({children, params}) {
  // Validate that the incoming `locale` parameter is valid
  if (!locales.includes(locale as any)) notFound();
 
  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  );
}

error.js

When an error file is defined, Next.js creates an error boundary within your layout (opens in a new tab) that wraps pages accordingly to catch runtime errors:

<LocaleLayout>
  <ErrorBoundary fallback={<Error />}>
    <Page />
  </ErrorBoundary>
</LocaleLayout>

Schematic component hierarchy that Next.js creates internally.

Since the error file must be defined as a Client Component, you have to use NextIntlClientProvider to provide messages in case the error file renders.

app/[locale]/layout.tsx
import pick from 'lodash/pick';
 
export default async function LocaleLayout({children}) {
  // ...
  const messages = useMessages();
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider
          locale={locale}
          messages={pick(messages, 'Error')}
        >
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

Once NextIntlClientProvider is in place, you can use functionality from next-intl in the error file:

app/[locale]/error.tsx
'use client';
 
import {useTranslations} from 'next-intl';
 
export default function Error({error, reset}) {
  const t = useTranslations('Error');
 
  return (
    <div>
      <h1>{t('title')}</h1>
      <button onClick={reset}>{t('retry')}</button>
    </div>
  );
}

Note that NextIntlClientProvider only provides the messages to your error page, but doesn't load any runtime code for processing translations on the client side. Only the error page will include this code, so the performance of other pages isn't affected.