Skip to content
Docs
Without i18n routing

App Router setup without i18n routing

Setting up an app without the i18n routing integration can be useful in the following cases:

  1. You'd like to provide a locale to next-intl, e.g. based on user settings
  2. Your app only supports a single language

This is the easiest way to get started with next-intl and requires no changes to the structure of your app.

Getting started

If you haven't done so already, create a Next.js app (opens in a new tab) that uses the App Router and run:

npm install next-intl

Now, we're going to create the following file structure:

├── messages
│   ├── en.json (1)
│   └── ...
├── next.config.mjs (2)
└── src
    ├── i18n.ts (3)
    └── app
        ├── layout.tsx (4)
        └── page.tsx (5)

Let's set up the files:

messages/en.json

Messages represent the translations that are available per language and can be provided either locally or loaded from a remote data source.

The simplest option is to add JSON files in your local project folder:

messages/en.json
{
  "HomePage": {
    "title": "Hello world!"
  }
}

next.config.mjs

Now, set up the plugin which creates an alias to provide a request-specific i18n configuration to Server Components (specified in the next step).

next.config.mjs
import createNextIntlPlugin from 'next-intl/plugin';
 
const withNextIntl = createNextIntlPlugin();
 
/** @type {import('next').NextConfig} */
const nextConfig = {};
 
export default withNextIntl(nextConfig);

i18n.ts

next-intl creates a request-scoped configuration object, which you can use to provide messages and other options based on the user's locale to Server Components.

src/i18n.ts
import {getRequestConfig} from 'next-intl/server';
 
export default getRequestConfig(async () => {
  // Provide a static locale, fetch a user setting,
  // read from `cookies()`, `headers()`, etc.
  const locale = 'en';
 
  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default
  };
});
Can I move this file somewhere else?

This file is supported out-of-the-box as ./i18n.ts both in the src folder as well as in the project root with the extensions .ts, .tsx, .js and .jsx.

If you prefer to move this file somewhere else, you can optionally provide a path to the plugin:

next.config.mjs
const withNextIntl = createNextIntlPlugin(
  // Specify a custom path here
  './somewhere/else/request.ts'
);

app/layout.tsx

The locale that was provided in i18n.ts is available via getLocale and can be used to configure the document language. Additionally, we can use this place to pass configuration from i18n.ts to Client Components via NextIntlClientProvider.

app/layout.tsx
import {NextIntlClientProvider} from 'next-intl';
import {getLocale, getMessages} from 'next-intl/server';
 
export default async function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  const locale = await getLocale();
 
  // Providing all messages to the client
  // side is the easiest way to get started
  const messages = await getMessages();
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

Note that NextIntlClientProvider automatically inherits configuration from i18n.ts here, but messages need to be passed explicitly.

app/page.tsx

Use translations in your page components or anywhere else!

app/page.tsx
import {useTranslations} from 'next-intl';
 
export default function HomePage() {
  const t = useTranslations('HomePage');
  return <h1>{t('title')}</h1>;
}

That's all it takes!

In case you ran into an issue, have a look at a working example:

💡

Next steps:

  • Usage guide: Learn how to format messages, dates and times

  • Workflows: Integrate deeply with TypeScript and other tools