Skip to content

Commit c40c5c9

Browse files
authored
feat: Inherit messages by default in NextIntlClientProvider (#1682)
This makes it easier to get started. If you don't want this behavior, you can still opt-out via `messages={null}`. **TODO** - [ ] Merge #1684 along with this
1 parent 67507cc commit c40c5c9

File tree

20 files changed

+94
-112
lines changed

20 files changed

+94
-112
lines changed

docs/src/pages/docs/environments/server-client-components.mdx

+6-12
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ Regarding performance, async functions and hooks can be used interchangeably. Th
115115

116116
## Using internationalization in Client Components
117117

118-
Depending on your situation, you may need to handle internationalization in Client Components. While providing all messages to the client side is typically the easiest way to [get started](/docs/getting-started/app-router#layout) and a reasonable approach for many apps, you can be more selective about which messages are passed to the client side if you're interested in optimizing the performance of your app.
118+
Depending on your situation, you may need to handle internationalization in Client Components. Providing all messages to the client side is the easiest way to get started, therefore `next-intl` automatically does this when you render [`NextIntlClientProvider`](/docs/usage/configuration#nextintlclientprovider). This is a reasonable approach for many apps.
119+
120+
However, you can be more selective about which messages are passed to the client side if you're interested in optimizing the performance of your app.
119121

120122
There are several options for using translations from `next-intl` in Client Components, listed here in order of enabling the best performance:
121123

122-
### Option 1: Passing translations to Client Components
124+
### Option 1: Passing translated labels to Client Components
123125

124126
The preferred approach is to pass the processed labels as props or `children` from a Server Component.
125127

@@ -278,8 +280,6 @@ In particular, page and search params are often a great option because they offe
278280

279281
### Option 3: Providing individual messages
280282

281-
To reduce bundle size, `next-intl` doesn't automatically provide [messages](/docs/usage/configuration#messages) to Client Components.
282-
283283
If you need to incorporate dynamic state into components that can not be moved to the server side, you can wrap these components with `NextIntlClientProvider` and provide the relevant messages.
284284

285285
```tsx filename="Counter.tsx"
@@ -315,22 +315,16 @@ An automatic, compiler-driven approach is being evaluated in [`next-intl#1`](htt
315315

316316
### Option 4: Providing all messages
317317

318-
If you're building a highly dynamic app where most components use React's interactive features, you may prefer to make all messages available to Client Components.
318+
If you're building a highly dynamic app where most components use React's interactive features, you may prefer to make all messages available to Client Components—this is the default behavior of `next-intl`.
319319

320320
```tsx filename="layout.tsx" /NextIntlClientProvider/
321321
import {NextIntlClientProvider} from 'next-intl';
322-
import {getMessages} from 'next-intl/server';
323322

324323
export default async function RootLayout(/* ... */) {
325-
// Receive messages provided in `i18n/request.ts`
326-
const messages = await getMessages();
327-
328324
return (
329325
<html lang={locale}>
330326
<body>
331-
<NextIntlClientProvider messages={messages}>
332-
{children}
333-
</NextIntlClientProvider>
327+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
334328
</body>
335329
</html>
336330
);

docs/src/pages/docs/getting-started/app-router/with-i18n-routing.mdx

+1-10
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ The `locale` that was matched by the middleware is available via the `locale` pa
186186

187187
```tsx filename="app/[locale]/layout.tsx"
188188
import {NextIntlClientProvider, Locale, hasLocale} from 'next-intl';
189-
import {getMessages} from 'next-intl/server';
190189
import {notFound} from 'next/navigation';
191190
import {routing} from '@/i18n/routing';
192191

@@ -201,24 +200,16 @@ export default async function LocaleLayout({
201200
notFound();
202201
}
203202

204-
// Providing all messages to the client
205-
// side is the easiest way to get started
206-
const messages = await getMessages();
207-
208203
return (
209204
<html lang={locale}>
210205
<body>
211-
<NextIntlClientProvider messages={messages}>
212-
{children}
213-
</NextIntlClientProvider>
206+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
214207
</body>
215208
</html>
216209
);
217210
}
218211
```
219212

220-
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n/request.ts` here, but `messages` need to be passed explicitly.
221-
222213
### `src/app/[locale]/page.tsx` [#page]
223214

224215
And that's it!

docs/src/pages/docs/getting-started/app-router/without-i18n-routing.mdx

+2-10
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ The `locale` that was provided in `i18n/request.ts` is available via `getLocale`
129129

130130
```tsx filename="app/layout.tsx"
131131
import {NextIntlClientProvider} from 'next-intl';
132-
import {getLocale, getMessages} from 'next-intl/server';
132+
import {getLocale} from 'next-intl/server';
133133

134134
export default async function RootLayout({
135135
children
@@ -138,24 +138,16 @@ export default async function RootLayout({
138138
}) {
139139
const locale = await getLocale();
140140

141-
// Providing all messages to the client
142-
// side is the easiest way to get started
143-
const messages = await getMessages();
144-
145141
return (
146142
<html lang={locale}>
147143
<body>
148-
<NextIntlClientProvider messages={messages}>
149-
{children}
150-
</NextIntlClientProvider>
144+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
151145
</body>
152146
</html>
153147
);
154148
}
155149
```
156150

157-
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n/request.ts` here, but `messages` need to be passed explicitly.
158-
159151
### `app/page.tsx` [#page]
160152

161153
Use translations in your page components or anywhere else!

docs/src/pages/docs/usage/configuration.mdx

+11-12
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ export default async function RootLayout(/* ... */) {
6464
return (
6565
<html lang={locale}>
6666
<body>
67-
<NextIntlClientProvider messages={messages}>
68-
{children}
69-
</NextIntlClientProvider>
67+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
7068
</body>
7169
</html>
7270
);
@@ -76,14 +74,15 @@ export default async function RootLayout(/* ... */) {
7674
These props are inherited if you're rendering `NextIntlClientProvider` from a Server Component:
7775

7876
1. `locale`
79-
2. `now`
80-
3. `timeZone`
81-
4. `formats`
77+
2. `messages`
78+
3. `now`
79+
4. `timeZone`
80+
5. `formats`
8281

8382
In contrast, these props can be provided as necessary:
8483

85-
1. `messages` (see [Internationalization in Client Components](/docs/environments/server-client-components#using-internationalization-in-client-components))
86-
2. `onError` and `getMessageFallback`
84+
1. `onError`
85+
2. `getMessageFallback`
8786

8887
Additionally, nested instances of `NextIntlClientProvider` will inherit configuration from their respective ancestors. Note however that individual props are treated as atomic, therefore e.g. `messages` need to be merged manually—if necessary.
8988

@@ -115,17 +114,16 @@ Once you have defined your client-side provider component, you can use it in a S
115114

116115
```tsx filename="layout.tsx"
117116
import {NextIntlClientProvider} from 'next-intl';
118-
import {getLocale, getMessages} from 'next-intl/server';
117+
import {getLocale} from 'next-intl/server';
119118
import IntlErrorHandlingProvider from './IntlErrorHandlingProvider';
120119

121120
export default async function RootLayout({children}) {
122121
const locale = await getLocale();
123-
const messages = await getMessages();
124122

125123
return (
126124
<html lang={locale}>
127125
<body>
128-
<NextIntlClientProvider messages={messages}>
126+
<NextIntlClientProvider>
129127
<IntlErrorHandlingProvider>{children}</IntlErrorHandlingProvider>
130128
</NextIntlClientProvider>
131129
</body>
@@ -380,13 +378,14 @@ const messages = await getMessages();
380378
```tsx
381379
import {NextIntlClientProvider} from 'next-intl';
382380
import {getMessages} from 'next-intl/server';
381+
import pick from 'lodash/pick';
383382

384383
async function Component({children}) {
385384
// Read messages configured via `i18n/request.ts`
386385
const messages = await getMessages();
387386

388387
return (
389-
<NextIntlClientProvider messages={messages}>
388+
<NextIntlClientProvider messages={pick(messages, ['Navigation'])}>
390389
{children}
391390
</NextIntlClientProvider>
392391
);

examples/example-app-router-migration/src/app/[locale]/layout.tsx

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {notFound} from 'next/navigation';
22
import {NextIntlClientProvider, hasLocale} from 'next-intl';
3-
import {getMessages} from 'next-intl/server';
43
import {ReactNode} from 'react';
54
import {routing} from '@/i18n/routing';
65

@@ -14,19 +13,13 @@ export default async function LocaleLayout({children, params}: Props) {
1413
notFound();
1514
}
1615

17-
// Providing all messages to the client
18-
// side is the easiest way to get started
19-
const messages = await getMessages();
20-
2116
return (
2217
<html lang={params.locale}>
2318
<head>
2419
<title>next-intl</title>
2520
</head>
2621
<body>
27-
<NextIntlClientProvider messages={messages}>
28-
{children}
29-
</NextIntlClientProvider>
22+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
3023
</body>
3124
</html>
3225
);

examples/example-app-router-mixed-routing/src/app/(public)/[locale]/layout.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Metadata} from 'next';
22
import {notFound} from 'next/navigation';
33
import {Locale, NextIntlClientProvider, hasLocale} from 'next-intl';
4-
import {getMessages, setRequestLocale} from 'next-intl/server';
4+
import {setRequestLocale} from 'next-intl/server';
55
import {ReactNode} from 'react';
66
import Document from '@/components/Document';
77
import {locales} from '@/config';
@@ -33,13 +33,9 @@ export default async function LocaleLayout({
3333
// Enable static rendering
3434
setRequestLocale(locale);
3535

36-
// Providing all messages to the client
37-
// side is the easiest way to get started
38-
const messages = await getMessages();
39-
4036
return (
4137
<Document locale={locale}>
42-
<NextIntlClientProvider messages={messages}>
38+
<NextIntlClientProvider>
4339
<div className="m-auto max-w-[60rem] p-4">
4440
<PublicNavigation />
4541
<div className="-mx-4 min-h-[200px] bg-slate-100 p-4">{children}</div>

examples/example-app-router-mixed-routing/src/app/app/layout.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Metadata} from 'next';
22
import {NextIntlClientProvider} from 'next-intl';
3-
import {getLocale, getMessages} from 'next-intl/server';
3+
import {getLocale} from 'next-intl/server';
44
import {ReactNode} from 'react';
55
import Document from '@/components/Document';
66
import AppNavigation from './AppNavigation';
@@ -18,13 +18,9 @@ export const metadata: Metadata = {
1818
export default async function LocaleLayout({children}: Props) {
1919
const locale = await getLocale();
2020

21-
// Providing all messages to the client
22-
// side is the easiest way to get started
23-
const messages = await getMessages();
24-
2521
return (
2622
<Document locale={locale}>
27-
<NextIntlClientProvider messages={messages}>
23+
<NextIntlClientProvider>
2824
<div className="flex">
2925
<div className="flex min-h-[100vh] w-[270px] shrink-0 flex-col justify-between bg-slate-100 p-8">
3026
<AppNavigation />

examples/example-app-router-single-locale/src/app/layout.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {NextIntlClientProvider} from 'next-intl';
2-
import {getLocale, getMessages} from 'next-intl/server';
2+
import {getLocale} from 'next-intl/server';
33
import {ReactNode} from 'react';
44

55
type Props = {
@@ -9,19 +9,13 @@ type Props = {
99
export default async function LocaleLayout({children}: Props) {
1010
const locale = await getLocale();
1111

12-
// Providing all messages to the client
13-
// side is the easiest way to get started
14-
const messages = await getMessages();
15-
1612
return (
1713
<html lang={locale}>
1814
<head>
1915
<title>next-intl</title>
2016
</head>
2117
<body>
22-
<NextIntlClientProvider messages={messages}>
23-
{children}
24-
</NextIntlClientProvider>
18+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
2519
</body>
2620
</html>
2721
);

examples/example-app-router-without-i18n-routing/src/app/layout.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import clsx from 'clsx';
22
import {Inter} from 'next/font/google';
33
import {NextIntlClientProvider} from 'next-intl';
4-
import {getLocale, getMessages} from 'next-intl/server';
4+
import {getLocale} from 'next-intl/server';
55
import {ReactNode} from 'react';
66
import './globals.css';
77

@@ -14,10 +14,6 @@ type Props = {
1414
export default async function LocaleLayout({children}: Props) {
1515
const locale = await getLocale();
1616

17-
// Providing all messages to the client
18-
// side is the easiest way to get started
19-
const messages = await getMessages();
20-
2117
return (
2218
<html lang={locale}>
2319
<head>
@@ -29,9 +25,7 @@ export default async function LocaleLayout({children}: Props) {
2925
inter.className
3026
)}
3127
>
32-
<NextIntlClientProvider messages={messages}>
33-
{children}
34-
</NextIntlClientProvider>
28+
<NextIntlClientProvider>{children}</NextIntlClientProvider>
3529
</body>
3630
</html>
3731
);

examples/example-app-router/src/components/BaseLayout.tsx

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {clsx} from 'clsx';
22
import {Inter} from 'next/font/google';
33
import {NextIntlClientProvider} from 'next-intl';
4-
import {getMessages} from 'next-intl/server';
54
import {ReactNode} from 'react';
65
import Navigation from '@/components/Navigation';
76

@@ -13,14 +12,10 @@ type Props = {
1312
};
1413

1514
export default async function BaseLayout({children, locale}: Props) {
16-
// Providing all messages to the client
17-
// side is the easiest way to get started
18-
const messages = await getMessages();
19-
2015
return (
2116
<html className="h-full" lang={locale}>
2217
<body className={clsx(inter.className, 'flex h-full flex-col')}>
23-
<NextIntlClientProvider messages={messages}>
18+
<NextIntlClientProvider>
2419
<Navigation />
2520
{children}
2621
</NextIntlClientProvider>

packages/next-intl/.size-limit.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const config: SizeLimitConfig = [
1010
name: "import {NextIntlClientProvider} from 'next-intl' (react-client)",
1111
import: '{NextIntlClientProvider}',
1212
path: 'dist/esm/production/index.react-client.js',
13-
limit: '1 KB'
13+
limit: '1.005 KB'
1414
},
1515
{
1616
name: "import * from 'next-intl' (react-server)",

0 commit comments

Comments
 (0)