-
-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prepare for async await headers()
in Next.js 15
#1375
Labels
enhancement
New feature or request
Comments
This was referenced Sep 27, 2024
Merged
amannn
added a commit
that referenced
this issue
Oct 1, 2024
This PR provides a new **`createNavigation`** function that supersedes the previously available APIs: 1. `createSharedPathnamesNavigation` 2. `createLocalizedPathnamesNavigation` The new function unifies the API for both use cases and also fixes a few quirks in the previous APIs. **Usage** ```tsx import {createNavigation} from 'next-intl/navigation'; import {defineRouting} from 'next-intl/routing'; export const routing = defineRouting(/* ... */); export const {Link, redirect, usePathname, useRouter} = createNavigation(routing); ``` (see the [updated navigation docs](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing/navigation)) **Improvements** 1. A single API can be used both for shared as well as localized pathnames. This reduces the API surface and simplifies the corresponding docs. 2. `Link` can now be composed seamlessly into another component with its `href` prop without having to add a generic type argument. 3. `getPathname` is now available for both shared as well as localized pathnames (fixes #785) 4. `router.push` and `redirect` now accept search params consistently via the object form (e.g. `router.push({pathname: '/users', query: {sortBy: 'name'})`)—regardless of if you're using shared or localized pathnames. 5. When using `localePrefix: 'as-necessary'`, the initial render of `Link` now uses the correct pathname immediately during SSR (fixes [#444](#444)). Previously, a prefix for the default locale was added during SSR and removed during hydration. Also `redirect` now gets the final pathname right without having to add a superfluous prefix (fixes [#1335](#1335)). The only exception is when you use `localePrefix: 'as-necessary'` in combination with `domains` (see [Special case: Using `domains` with `localePrefix: 'as-needed'`](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing#domains-localeprefix-asneeded)) 6. `Link` is now compatible with the `asChild` prop of Radix Primitives when rendered in RSC (see [#1322](#1322)) **Migrating to `createNavigation`** `createNavigation` is generally considered a drop-in replacement, but a few changes might be necessary: 1. `createNavigation` is expected to receive your complete routing configuration. Ideally, you define this via the [`defineRouting`](https://next-intl-docs.vercel.app/docs/routing#define-routing) function and pass the result to `createNavigation`. 2. If you've used `createLocalizedPathnamesNavigation` and have [composed the `Link` with its `href` prop](https://next-intl-docs.vercel.app/docs/routing/navigation#link-composition), you should no longer provide the generic `Pathname` type argument (see [updated docs](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing/navigation#link-composition)). ```diff - ComponentProps<typeof Link<Pathname>> + ComponentProps<typeof Link> ``` 3. If you've used [`redirect`](https://next-intl-docs.vercel.app/docs/routing/navigation#redirect), you now have to provide an explicit locale (even if it's just [the current locale](https://next-intl-docs.vercel.app/docs/usage/configuration#locale)). This change was necessary for an upcoming change in Next.js 15 where `headers()` turns into a promise (see [#1375](#1375) for details). ```diff - redirect('/about') + redirect({pathname: '/about', locale: 'en'}) ``` 4. If you've used [`getPathname`](https://next-intl-docs.vercel.app/docs/routing/navigation#getpathname) and have previously manually prepended a locale prefix, you should no longer do so—`getPathname` now takes care of this depending on your routing strategy. ```diff - '/'+ locale + getPathname(/* ... */) + getPathname(/* ... */); ``` 5. If you're using a combination of `localePrefix: 'as-necessary'` and `domains` and you're using `getPathname`, you now need to provide a `domain` argument (see [Special case: Using `domains` with `localePrefix: 'as-needed'`](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing#domains-localeprefix-asneeded))
The following PRs will resolve this: |
amannn
added a commit
that referenced
this issue
Oct 2, 2024
…js 15 support (#1383) Since [Next.js is switching `headers()` to be `async`](vercel/next.js#68812), the `locale` that is passed to `getRequestConfig` needs to be replaced by an awaitable alternative. Note that this is only relevant for your app in case you're using i18n routing. ## tldr; Switch to the new API and call it a day: ```diff export default getRequestConfig(async ({ - locale + requestLocale }) => { + const locale = await requestLocale; // ... }); ``` If your app worked well before, then this is a 1:1 switch and will get your app in shape for Next.js 15. ## Details The new `requestLocale` parameter also offered a chance to get in some enhancements for edge cases that were previously harder to support. Therefore, the following migration is generally recommended: **Before:** ```tsx import {notFound} from 'next/navigation'; import {getRequestConfig} from 'next-intl/server'; import {routing} from './routing'; export default getRequestConfig(async ({locale}) => { // Validate that the incoming `locale` parameter is valid if (!routing.locales.includes(locale as any)) notFound(); return { // ... }; }); ``` **After:** ```tsx filename="src/i18n/request.ts" import {getRequestConfig} from 'next-intl/server'; import {routing} from './routing'; export default getRequestConfig(async ({requestLocale}) => { // This typically corresponds to the `[locale]` segment let locale = await requestLocale; // Ensure that the incoming locale is valid if (!locale || !routing.locales.includes(locale as any)) { locale = routing.defaultLocale; } return { locale, // ... }; }); ``` The differences are: 1. `requestLocale` is a promise that needs to be awaited 2. The resolved value can be `undefined`—therefore a default should be supplied. The default assignment allows handling cases where an error would be thrown previously, e.g. when using APIs like `useTranslations` on a global language selection page at `app/page.tsx`. 3. The `locale` should be returned (since you can now adjust it in the function body). 4. We now recommend calling `notFound()` in response to an invalid `[locale]` param in [`app/[locale]/layout.tsx`](https://next-intl-docs-git-feat-async-request-locale-next-intl.vercel.app/docs/getting-started/app-router/with-i18n-routing#layout) instead of in `i18n/request.ts`. This unlocks another use case, where APIs like `useTranslations` can now be used on a global `app/not-found.tsx` page. See also the [updated getting started docs](https://next-intl-docs-git-feat-async-request-locale-next-intl.vercel.app/docs/getting-started/app-router/with-i18n-routing#i18n-request). Note that this change is non-breaking, but the synchronously available `locale` is now considered deprecated and will be removed in a future major version. Contributes to #1375 Addresses #1355
This was referenced Oct 2, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is your feature request related to a problem? Please describe.
Ref.: vercel/next.js#68812
next-intl
currently relies on reading a request header to detect the current locale when relevant APIs are called in a Server Component. Note that this by itself is a workaround, ideally the locale could be retrieved from a param (see vercel/next.js#58862).The APIs
next-intl
offers can be put in three categories:useTranslations
&useFormatter
)useLocale
)Link
—these are lightweight wrappers for navigation APIs from Next.js and automatically incorporate localization of pathnames like locale prefixes)The first two categories use APIs that have signatures that are either hooks or awaitable async functions—so those should be fine to update.
In the third category, the APIs are a bit more diverse. However, most APIs can be adapted as necessary:
Link
component can be turnedasync
to useawait getLocale()
internallyuseRouter
&usePathname
can consume a promise viause
getPathname
requires an explicit locale anywayOnly one API stands out as problematic:
redirect
This is a function that can be called in the following environments:
async
componentasync
componentSince
use(…)
doesn't work inasync
components, we'd have to use an awaitable API for (1). However, for the other call sites, a non-awaitable API would be more ergonomic (otherwise the user would have to wrap inuse
)Describe the solution you'd like
I can think of the following solutions to address this:
redirect
:redirect({locale: 'en', href: '/about'})
. To retrieve the locale, it'd be the job of the user to either callasync getLocale()
oruseLocale()
depending on the calling component.redirect('/about')
redirect
awaitablelocale
(mostly)Another implication is likely that the user can't read the
locale
that's passed toi18n/request.ts
synchronously. We might have to replace this with something promise-based (await locale
orawait getLocale()
).Open questions
params
became async, it seems so. Due to this, it's probably a good idea to plan long term and assume that accessing a locale will always be async.use()
work in the future in async RSC? If yes, then we could receive a default for the locale if we choose approach (1) from above and make thelocale
property optional. If we go for (2) we'd needlessly have an async API.Moving forward
As matters stand currently, it seems like (1) will be more future-proof. Also, in case
use()
becomes available in async Server Components in the future, we can put back a default for the current locale in a later release.Note that the navigation APIs are currently being reworked in #1316, so now would be a good point to adjust for the future.
The text was updated successfully, but these errors were encountered: