Markdown (MDX)
Especially for sites where the content varies significantly by locale and may require a different structure, it can be helpful to use Markdown or MDX (opens in a new tab) to provide your localized content.
While you can provide Markdown or MDX content from any remote source, the simplest way to get started is to provide the files locally in your project directory:
content
├── en
│ └── home.mdx
└── de
└── home.mdx
Individual files might look like this:
# Home
Welcome to my site!
<Portrait />
By using @mdx-js/mdx
(opens in a new tab), you can parse and render the content in a Server Component:
import fs from 'fs/promises';
import {EvaluateOptions, evaluate} from '@mdx-js/mdx';
import {getLocale} from 'next-intl/server';
import * as runtime from 'react/jsx-runtime';
import Portrait from './Portrait';
async function MDXContent({filename}: {filename: string}) {
// Custom components that can be
// referenced in the content
const components = {Portrait};
try {
const locale = await getLocale();
const file = await fs.readFile(`./content/${locale}/${filename}`, 'utf-8');
const {default: Content} = await evaluate(file, runtime as EvaluateOptions);
return <Content components={components} />;
} catch (error) {
notFound();
}
}
export default async function HomePage() {
return <MDXContent filename="home.mdx" />;
}
Components that invoke hooks from next-intl
like useTranslations
can naturally be used in MDX content and will respect the user's locale.
Note that MDX compiles to JavaScript and is dynamically evaluated. You should be sure to only load MDX content from a trusted source, otherwise this can lead to arbitrary code execution (opens in a new tab).
Is MDX required to format rich text?
Not at all! Messages support rich text syntax, which can be used to provide formatting, structure and embedding of components.
Can I also use @next/mdx
?
@next/mdx
(opens in a new tab) requires to set up individual MDX files as pages in the app
directory.
For a multi-lingual app, this might look like this:
src
└── app
├── en
│ └── page.mdx
└── de
└── page.mdx
While this approach can also work for you, it comes with the following gotchas:
- If your app has some pages in a
[locale]
folder and some statically defined MDX pages, your pages will be distributed across different folders. Due to this, e.g. a shared layout fromapp/[locale]/layout.tsx
won't be applied forapp/en/page.tsx
. Therefore this approach might be more relevant for sites where you define all pages in MDX exclusively. - Since there's no dynamic
[locale]
segment,next-intl
can't read from this in Client Components to retrieve the locale. Instead, you can addNextIntlClientProvider
in a layout within the respective locale folder (e.g.app/en/layout.tsx
).
Do I need to put my MDX files in my project folder?
@mdx-js/mdx
(opens in a new tab) allows processing MDX content that was fetched from an arbitrary data source. If you'd like to allow translators to work on MDX files, you can for example consider uploading them to a translation management system like Crowdin (opens in a new tab).