Demo of Next.js 16 cacheComponents with next-intl, using next/root-params to access the [locale] segment inside "use cache" functions.
With cacheComponents enabled, cached components cannot access headers() — which is how next-intl normally resolves the locale. Previously, the workaround was to prop-drill the locale from the page (which extracts it from params) into each cached component:
// Page extracts locale from params and passes it down
export default async function Page({params}: PageProps<'/[locale]'>) {
const {locale} = await params;
return <CachedComponent locale={locale} />;
}
async function CachedComponent({locale}: {locale: string}) {
'use cache';
const t = await getTranslations({locale, namespace: 'MyNamespace'});
// ...
}This works but requires plumbing locale through every cached component.
The [locale] segment is a root parameter — a dynamic segment before the root layout. With experimental.rootParams enabled, import { locale } from 'next/root-params' can be called from any Server Component, including inside "use cache" boundaries. The root param value automatically becomes a cache key.
import {locale as rootLocale} from 'next/root-params';
async function CachedComponent() {
'use cache';
const locale = await rootLocale();
const t = await getTranslations({locale, namespace: 'MyNamespace'});
// ...
}No props needed — the component is self-contained.
- Dynamic Component: Uses
getTranslations('IndexPage')with only namespace parameter, which internally reads fromheaders(). This component is not cacheable and runs on every request. - Cached Component: Reads locale directly from
next/root-paramsinside"use cache"and passes it togetTranslations({locale, namespace}). The root param value automatically becomes a cache key — no prop-drilling needed.
next.config.ts— enablescacheComponentsandexperimental.rootParamssrc/app/[locale]/layout.tsx— root layout usingawait locale()fromnext/root-paramssrc/app/[locale]/page.tsx— page with cached and dynamic components