-
-
Notifications
You must be signed in to change notification settings - Fork 246
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
Comments
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:
More details: #1391 |
…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
Is this resolved with the canary version of next-intl? I am still getting
|
Please see here: #1391 |
Hey thanks for the reply! I am unfortunately still having that error after making the changes
Will the new canary release on that PR solve the problem? Also I am still really confused by the approach of adding |
@tonyabracadabra Did you upgrade to the new APIs in the canary version? (see also #1391 (comment)) I tried recently with a Next.js 15 canary version and was able to get rid of all warnings. Can you try to diagnose where exactly you're seeing a warning and what causes it?
That's unfortunately a different story, see #663 |
@amannn Have you seen vercel/next.js#58862 (comment)? |
@imranbarbhuiya Yep, we had a brief exchange on X: https://x.com/jamannnnnn/status/1842179418657116244. It's essentially the same approach as @danielkoller I can't really see an issue in that code sample you've provided. Maybe the error is triggered by another component? I'll soon have the examples in this repo updated for Next.js 15, maybe that can help for reference. |
Next.js 15 is supported now without warnings:
However, please consider: #1442. Due to this, I'm waiting for now with updating examples and the official bug repro. |
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()
).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: