diff --git a/docs/pages/docs/environments/server-client-components.mdx b/docs/pages/docs/environments/server-client-components.mdx index 922dd7fef..c07d28929 100644 --- a/docs/pages/docs/environments/server-client-components.mdx +++ b/docs/pages/docs/environments/server-client-components.mdx @@ -127,7 +127,7 @@ Depending on your situation, you may need to handle internationalization in Clie The preferred approach is to pass the processed labels as props or `children` from a Server Component. -```tsx filename="[locale]/faq/page.tsx" +```tsx filename="[locale]/faq/page.tsx" {10-12} import {useTranslations} from 'next-intl'; import Expandable from './Expandable'; @@ -165,10 +165,91 @@ function Expandable({title, children}) { } ``` -As you see, we can use interactive features from React like `useState` on translated content, even though the translation only runs on the server side. +By doing this, we can use interactive features from React like `useState` on translated content, even though the translation only runs on the server side. Learn more in the Next.js docs: [Passing Server Components to Client Components as Props](https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#supported-pattern-passing-server-components-to-client-components-as-props) +
+Example: How can I implement a form? + +Forms need client-side state for showing loading indicators and validation errors. + +To keep internationalization on the server side, it can be helpful to structure your components in a way where the interactive parts are moved out to leaf components instead of marking the whole form with `'use client';`. + +**Example:** + +```tsx filename="app/register/page.tsx" +import {useTranslations} from 'next-intl'; + +// A Client Component, so that it can use `useFormState` to +// potentially display errors received after submission. +import RegisterForm from './RegisterForm'; + +// A Client Component, so that it can use `useFormStatus` +// to disable the input field during submission. +import FormField from './FormField'; + +// A Client Component, so that it can use `useFormStatus` +// to disable the submit button during submission. +import FormSubmitButton from './FormSubmitButton'; + +export default function RegisterPage() { + const t = useTranslations('RegisterPage'); + + function registerUser() { + 'use server'; + // ... + } + + return ( + + + + + + + + ); +} +``` + +
+ +
+Example: How can I implement a locale switcher? + +If you implement a locale switcher as an interactive select, you can keep internationalization on the server side by rendering the labels from a Server Component and only marking the select element as a Client Component. + +```tsx filename="LocaleSwitcher.tsx" +import {useLocale, useTranslations} from 'next-intl'; +import {locales} from 'config'; + +// A Client Component that registers an event listener for +// the `change` event of the select, uses `useRouter` +// to change the locale and uses `useTransition` to display +// a loading state during the transition. +import LocaleSwitcherSelect from './LocaleSwitcherSelect'; + +export default function LocaleSwitcher() { + const t = useTranslations('LocaleSwitcher'); + const locale = useLocale(); + + return ( + + {locales.map((cur) => ( + + ))} + + ); +} +``` + +[Example implementation](https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/components/LocaleSwitcher.tsx) ([demo](https://next-intl-example-app-router.vercel.app/en)) + +
+ ### Option 2: Moving state to the server side You might run into cases where you have dynamic state, such as pagination, that should be reflected in translated messages. diff --git a/examples/example-app-router/src/components/LocaleSwitcher.tsx b/examples/example-app-router/src/components/LocaleSwitcher.tsx index ac5f7821c..2b07d5a35 100644 --- a/examples/example-app-router/src/components/LocaleSwitcher.tsx +++ b/examples/example-app-router/src/components/LocaleSwitcher.tsx @@ -1,4 +1,5 @@ import {useLocale, useTranslations} from 'next-intl'; +import {locales} from 'config'; import LocaleSwitcherSelect from './LocaleSwitcherSelect'; export default function LocaleSwitcher() { @@ -7,7 +8,7 @@ export default function LocaleSwitcher() { return ( - {['en', 'de'].map((cur) => ( + {locales.map((cur) => (