Docs
/
Next.js 13 (preview)

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.

  1. Create the following file structure:
├── messages (1)
│   ├── en.json
│   └── ...
├── app
│   ├── [locale]
│   │   ├── layout.tsx (2)
│   │   └── page.tsx (3)
│   └── ...
└── middleware.tsx (4)
  1. Set up messages for a language, e.g. in messages/en.json (1):
{
  "Index": {
    "title": "Hello world!"
  }
}
  1. Configure NextIntlServerProvider in app/[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>
  );
}
  1. 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>;
}
  1. 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.