Skip to content

Commit

Permalink
docs: Add error handling docs
Browse files Browse the repository at this point in the history
  • Loading branch information
amannn committed Jul 13, 2023
1 parent 9f8fb6e commit 97a7beb
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/pages/docs/environments/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"index": "Overview",
"server-client-components": "Server & Client Components",
"metadata-route-handlers": "Metadata & Route Handlers",
"error-files": "Error files (e.g. not-found)",
"core-library": "Core library"
}
123 changes: 123 additions & 0 deletions docs/pages/docs/environments/error-files.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import VersionTabs from 'components/VersionTabs';
import Callout from 'components/Callout';

# Internationalization in Next.js error files

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

1. [`not-found.js`](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)
2. [`error.js`](https://nextjs.org/docs/app/api-reference/file-conventions/error)

While `not-found.js` can either be a Server or Client Component, the latter two files require to be Client Components. Therefore depending on the file, you have to make sure that you're [providing configuration like messages](/docs/configuration#client-server-components) accordingly.

This page provides practical guides for these cases.

<Callout>
Have a look at [the App Router example](/examples/app-router) to explore a
working app with error handling.
</Callout>

## `not-found.js`

Next.js renders the nearest `not-found` page when a route segment throws a [`notFound` function](https://nextjs.org/docs/app/api-reference/functions/not-found). Since we rely on the `[locale]` segment to localize our app, the `not-found` page needs to be located within this segment.

```tsx filename="app/[locale]/not-found.tsx"
import {useTranslations} from 'next-intl';
import PageLayout from 'components/PageLayout';

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 thrown from within a route, not for all unknown routes in general.

### Catching unknown routes

To catch unknown routes too, we can define a catch-all route that explicitly throws the `notFound` function.

```tsx filename="app/[locale]/[...rest]/page.tsx"
import {notFound} from 'next/navigation';

export default function CatchAllPage() {
notFound();
}
```

After this change, all routes that are matched within the `[locale]` segment will render the defined `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](/docs/routing/middleware) (depending on your `matcher` config), there's no locale associated with the request. You can add a root `not-found` page to handle these cases too (e.g. `/unknown.txt`).

```tsx filename="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>
);
}
```

## `error.js`

When an unexpected runtime error is encountered within your components, Next.js will gracefully handle the error by [using the closest `error` file](https://nextjs.org/docs/app/building-your-application/routing/error-handling). When an `error` file is defined, Next.js will create [an error boundary within your layout that wraps pages accordingly](https://nextjs.org/docs/app/building-your-application/routing/error-handling#how-errorjs-works).

Since the `error` file must be defined as a Client Component, you have to use [`NextIntlClientProvider`](/docs/configuration#client-server-components) to provide messages in case the `error` file renders.

If you've [set up `next-intl` to be used in Client Components](http://localhost:3001/docs/getting-started/app-router-client-components), this is already the case and there's no additional setup needed. If you're using [the Server Components beta](/docs/getting-started/app-router-server-components), you have to provide the relevant messages in the wrapping layout.

```tsx filename="app/[locale]/layout.tsx"
import pick from 'lodash/pick';

// (only necessary when using the Server Components beta)

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 `next-intl` APIs in the `error` file:

```tsx filename="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>
);
}
```
7 changes: 7 additions & 0 deletions docs/pages/docs/environments/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
RectangleGroupIcon,
DocumentTextIcon,
BoltIcon,
CodeBracketIcon
} from '@heroicons/react/24/outline';
import {Card} from 'nextra-theme-docs';
Expand All @@ -22,6 +23,12 @@ The `next-intl` APIs are available in the following environments:
title="Metadata API & Route Handlers"
href="/docs/environments/metadata-route-handlers"
/>
<Card
arrow
icon={<BoltIcon />}
title="Error files (e.g. not-found)"
href="/docs/environments/error-files"
/>
<Card
arrow
icon={<CodeBracketIcon />}
Expand Down
4 changes: 4 additions & 0 deletions examples/example-next-13/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"title": "Über",
"description": "<p>Auch das Routing ist internationalisiert.</p><p>Wenn du die Standardsprache Englisch verwendest, siehst du <code>/about</code> in der Adressleiste des Browsers auf dieser Seite.</p><p>Wenn du die Sprache auf Deutsch änderst, wird die URL mit der Locale ergänzt (<code>/de/about</code>).</p>"
},
"Error": {
"title": "Etwas ist schief gelaufen!",
"description": "<p>Es ist leider ein Problem aufgetreten.</p><p>Du kannst versuchen <retry>diese Seite neu zu laden</retry>.</p>"
},
"NotFoundPage": {
"title": "Seite nicht gefunden",
"description": "Bitte überprüfe die Addressleiste deines Browsers oder verwende die Navigation um zu einer bekannten Seite zu wechseln."
Expand Down
4 changes: 4 additions & 0 deletions examples/example-next-13/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"title": "About",
"description": "<p>The routing is internationalized too.</p><p>If you're using the default language English, you'll see <code>/about</code> in the browser address bar on this page.</p><p>If you change the locale to German, the URL is prefixed with the locale (<code>/de/about</code>).</p>"
},
"Error": {
"title": "Something went wrong!",
"description": "<p>We've unfortunately encountered an error.</p><p>You can try to <retry>reload the page</retry> you were visiting.</p>"
},
"NotFoundPage": {
"title": "Page not found",
"description": "Please double-check the browser address bar or use the navigation to go to a known page."
Expand Down
37 changes: 37 additions & 0 deletions examples/example-next-13/src/app/[locale]/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import {useTranslations} from 'next-intl';
import {useEffect} from 'react';
import PageLayout from 'components/PageLayout';

type Props = {
error: Error;
reset(): void;
};

export default function Error({error, reset}: Props) {
const t = useTranslations('Error');

useEffect(() => {
console.error(error);
}, [error]);

return (
<PageLayout title={t('title')}>
<div>
{t.rich('description', {
p: (chunks) => <p className="mt-4">{chunks}</p>,
retry: (chunks) => (
<button
className="text-white underline underline-offset-2"
onClick={reset}
type="button"
>
{chunks}
</button>
)
})}
</div>
</PageLayout>
);
}
1 change: 0 additions & 1 deletion examples/example-next-13/src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function IndexPage() {
<PageLayout title={t('title')}>
<p className="max-w-[590px]">
{t.rich('description', {
p: (chunks) => <p className="mt-4">{chunks}</p>,
code: (chunks) => (
<code className="font-mono text-white">{chunks}</code>
)
Expand Down
5 changes: 3 additions & 2 deletions examples/example-next-13/src/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import Error from 'next/error';

// This page renders when a route is requested that doesn't match the
// middleware and therefore doesn't have a locale associated with it.
// 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 (
Expand Down

0 comments on commit 97a7beb

Please sign in to comment.