Next.js 13 (preview)
Next.js 13 introduces the app
directory, which includes support for React Server Components. next-intl
is adopting the new capabilities and is currently offering a preview to early adopters, who are already building apps with the app
directory.
The app
directory is currently in beta, patterns are still emerging and APIs
may change. Please use this at your own risk, knowing that you might have
to face a migration effort when the app
directory becomes stable.
next-intl
tries to stay up to date with the latest developments on the
Next.js side, but during this period there can be unexpected issues.
Current pre-release:
npm install next-intl@2.10.0-alpha.5
This pre-release was tested with next@13.0.6
.
Getting started
Create a Next.js 13 app that uses the app
directory if you haven't done so already. The goal is to prefix all routes with the locale
, so we can retrieve it as a dynamic segment and use it to configure next-intl
.
- Create the following file structure:
├── messages (1)
│ ├── en.json
│ └── ...
├── app
│ ├── [locale]
│ │ ├── layout.tsx (2)
│ │ └── page.tsx (3)
│ └── ...
└── middleware.tsx (4)
- Set up messages for a language, e.g. in
messages/en.json
(1):
{
"Index": {
"title": "Hello world!"
}
}
- Configure
NextIntlServerProvider
inapp/[locale]/layout.tsx
(2):
import {NextIntlServerProvider} from 'next-intl/server';
import {notFound} from 'next/navigation';
export default async function LocaleLayout({children, params: {locale}}) {
let messages;
try {
messages = (await import(`../../messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
return (
<NextIntlServerProvider locale={locale} messages={messages}>
{children}
</NextIntlServerProvider>
);
}
- Use your messages in
app/[locale]/page.tsx
(3):
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
- Create a middleware that handles redirects:
import {createIntlMiddleware} from 'next-intl/server';
// This middleware intercepts requests to `/` and will redirect
// to one of the configured locales instead (e.g. `/en`). A cookie
// is set in the background, so if the user switches to a new
// language, this language will take precedence from now on.
export default createIntlMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en'
});
That's all you need to do to start using translations in Server Components!
If you've encountered an issue, you can explore the code for a working example (demo).
Using translations in Client Components
If you need to use translations in Client Components, the best approach is to pass the generated labels as props.
// app/[locale]/page.tsx
import {useTranslations} from 'next-intl';
import InteractiveClientComponent from './InteractiveClientComponent';
export default function Index() {
const t = useTranslations('Index');
return <InteractiveClientComponent title={t('title')} />;
}
// app/[locale]/InteractiveClientComponent.tsx
'use client';
import {useEffect} from 'react';
function InteractiveClientComponent({title}) {
useEffect(() => alert(title), []);
return <h1>{title}</h1>;
}
This way your messages never leave the server and the client only needs to load the code that is necessary for initializing your interactive components.
If you absolutely need to use functionality from next-intl
on the client side, you can wrap the respective components with NextIntlClientProvider
(example code). Note however that this will increase your client bundle size.
Migrating from the pages
folder
If you have existing code from the pages
folder that you want to migrate, you can use NextIntlClientProvider
(instead of NextIntlServerProvider
) in app/[locale]/layout.tsx
:
import {NextIntlClientProvider} from 'next-intl/client';
import {notFound} from 'next/navigation';
export default async function LocaleLayout({children, params: {locale}}) {
let messages;
try {
messages = (await import(`../../../messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
return (
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
);
}
By doing this, the messages become available for all Client Components that are rendered in your app. Note that you have to make use of 'use client';
in all components that use features from next-intl
if you use this approach.
// app/[locale]/page.tsx
'use client';
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
If you're transitioning your components to the app
directory, you can use both providers to have messages available in Server as well as Client Components.
Also note that internationalized routing is no longer supported natively by Next.js, therefore you should use the middleware mentioned above.
Providing feedback
If you have feedback about using next-intl
in the app
dir, I'd be happy to hear from you! Feel free to leave feedback in the PR which implements the new features.