Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add error handling docs #401

Merged
merged 3 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}
129 changes: 129 additions & 0 deletions docs/pages/docs/environments/error-files.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
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)

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 closest `not-found` page when a route segment calls the [`notFound` function](https://nextjs.org/docs/app/api-reference/functions/not-found). Since you rely on the `[locale]` segment to localize your app, the `not-found` page needs to be located within this route segment too.

```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 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.

```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 `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 (e.g. `/unknown.txt`). You can add a root `not-found` page to handle these cases too.

```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 `error` file is defined, Next.js will create [an error boundary within your layout](https://nextjs.org/docs/app/building-your-application/routing/error-handling#how-errorjs-works) that wraps pages accordingly to catch runtime errors:

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

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) though, 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