Skip to content
Docs
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.

Tip: You can have a look at the App Router example to explore a working app with error handling.

not-found.js

💡

This section is only relevant if you're using i18n routing.

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 (e.g. /en/unknown).

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';
 
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 in i18n.ts when we detect an incoming locale param that isn't a valid locale.

i18n.ts
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
 
// Can be imported from a shared config
const locales = ['en', 'de'];
 
export default getRequestConfig(async ({locale}) => {
  // Validate that the incoming `locale` parameter is valid
  if (!locales.includes(locale as any)) notFound();
 
  return {
    // ...
  };
});

Note that next-intl will also call the notFound function internally when it tries to resolve a locale for component usage, but can not find one attached to the request (either from the middleware, or manually via unstable_setRequestLocale (opens in a new tab)).

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:

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

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.

layout.tsx
import pick from 'lodash/pick';
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
 
export default async function RootLayout(/* ... */) {
  const messages = await getMessages();
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider
          locale={locale}
          // Make sure to provide at least the messages for `Error`
          messages={pick(messages, 'Error')}
        >
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

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

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 error.tsx is loaded right after your app has initialized. If your app is performance-sensitive and you want to avoid loading translation functionality from next-intl as part of this bundle, you can export a lazy reference from your error file:

error.tsx
'use client';
 
import {lazy} from 'react';
 
// Move error content to a separate chunk and load it only when needed
export default lazy(() => import('./Error'));