-
Notifications
You must be signed in to change notification settings - Fork 27.2k
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
feat: rootParams (experimental) #72837
Conversation
24269b3
to
d509ed4
Compare
Tests Passed |
Stats from current PRDefault Build (Increase detected
|
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
buildDuration | 19.7s | 17.4s | N/A |
buildDurationCached | 16.6s | 14.1s | N/A |
nodeModulesSize | 410 MB | 410 MB | |
nextStartRea..uration (ms) | 490ms | 493ms | N/A |
Client Bundles (main, webpack)
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
1187-HASH.js gzip | 51 kB | 51 kB | N/A |
8276.HASH.js gzip | 169 B | 168 B | N/A |
8377-HASH.js gzip | 5.36 kB | 5.36 kB | N/A |
bccd1874-HASH.js gzip | 53 kB | 53 kB | N/A |
framework-HASH.js gzip | 57.5 kB | 57.5 kB | N/A |
main-app-HASH.js gzip | 232 B | 235 B | N/A |
main-HASH.js gzip | 34.1 kB | 34 kB | N/A |
webpack-HASH.js gzip | 1.71 kB | 1.71 kB | N/A |
Overall change | 0 B | 0 B | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 39.4 kB | 39.4 kB | ✓ |
Client Pages
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
_app-HASH.js gzip | 193 B | 193 B | ✓ |
_error-HASH.js gzip | 193 B | 193 B | ✓ |
amp-HASH.js gzip | 512 B | 510 B | N/A |
css-HASH.js gzip | 343 B | 342 B | N/A |
dynamic-HASH.js gzip | 1.84 kB | 1.84 kB | ✓ |
edge-ssr-HASH.js gzip | 265 B | 265 B | ✓ |
head-HASH.js gzip | 363 B | 362 B | N/A |
hooks-HASH.js gzip | 393 B | 392 B | N/A |
image-HASH.js gzip | 4.49 kB | 4.49 kB | N/A |
index-HASH.js gzip | 268 B | 268 B | ✓ |
link-HASH.js gzip | 2.35 kB | 2.34 kB | N/A |
routerDirect..HASH.js gzip | 328 B | 328 B | ✓ |
script-HASH.js gzip | 397 B | 397 B | ✓ |
withRouter-HASH.js gzip | 323 B | 326 B | N/A |
1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
Overall change | 3.59 kB | 3.59 kB | ✓ |
Client Build Manifests
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
_buildManifest.js gzip | 749 B | 746 B | N/A |
Overall change | 0 B | 0 B | ✓ |
Rendered Page Sizes
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
index.html gzip | 524 B | 524 B | ✓ |
link.html gzip | 539 B | 538 B | N/A |
withRouter.html gzip | 519 B | 520 B | N/A |
Overall change | 524 B | 524 B | ✓ |
Edge SSR bundle Size
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
edge-ssr.js gzip | 128 kB | 128 kB | N/A |
page.js gzip | 204 kB | 204 kB | N/A |
Overall change | 0 B | 0 B | ✓ |
Middleware size
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
middleware-b..fest.js gzip | 671 B | 668 B | N/A |
middleware-r..fest.js gzip | 155 B | 156 B | N/A |
middleware.js gzip | 31.2 kB | 31.2 kB | N/A |
edge-runtime..pack.js gzip | 844 B | 844 B | ✓ |
Overall change | 844 B | 844 B | ✓ |
Next Runtimes
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
523-experime...dev.js gzip | 322 B | 322 B | ✓ |
523.runtime.dev.js gzip | 314 B | 314 B | ✓ |
app-page-exp...dev.js gzip | 323 kB | 323 kB | N/A |
app-page-exp..prod.js gzip | 127 kB | 127 kB | N/A |
app-page-tur..prod.js gzip | 140 kB | 140 kB | N/A |
app-page-tur..prod.js gzip | 135 kB | 135 kB | N/A |
app-page.run...dev.js gzip | 314 kB | 314 kB | N/A |
app-page.run..prod.js gzip | 123 kB | 123 kB | N/A |
app-route-ex...dev.js gzip | 37.4 kB | 37.4 kB | N/A |
app-route-ex..prod.js gzip | 25.5 kB | 25.5 kB | N/A |
app-route-tu..prod.js gzip | 25.5 kB | 25.5 kB | N/A |
app-route-tu..prod.js gzip | 25.3 kB | 25.3 kB | N/A |
app-route.ru...dev.js gzip | 39 kB | 39 kB | N/A |
app-route.ru..prod.js gzip | 25.3 kB | 25.3 kB | N/A |
pages-api-tu..prod.js gzip | 9.69 kB | 9.69 kB | ✓ |
pages-api.ru...dev.js gzip | 11.6 kB | 11.6 kB | ✓ |
pages-api.ru..prod.js gzip | 9.68 kB | 9.68 kB | ✓ |
pages-turbo...prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
pages.runtim...dev.js gzip | 27.4 kB | 27.4 kB | ✓ |
pages.runtim..prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
server.runti..prod.js gzip | 916 kB | 916 kB | ✓ |
Overall change | 1.02 MB | 1.02 MB | ✓ |
build cache Overall increase ⚠️
vercel/next.js canary | vercel/next.js 11-12-feat_rootparams | Change | |
---|---|---|---|
0.pack gzip | 2.06 MB | 2.06 MB | |
index.pack gzip | 71.9 kB | 72 kB | |
Overall change | 2.13 MB | 2.13 MB |
Diff details
Diff for middleware.js
Diff too large to display
Diff for edge-ssr.js
Diff too large to display
Diff for 1187-HASH.js
Diff too large to display
Diff for main-HASH.js
Diff too large to display
Diff for app-page-exp..ntime.dev.js
Diff too large to display
Diff for app-page-exp..time.prod.js
Diff too large to display
Diff for app-page-tur..time.prod.js
Diff too large to display
Diff for app-page-tur..time.prod.js
Diff too large to display
Diff for app-page.runtime.dev.js
Diff too large to display
Diff for app-page.runtime.prod.js
Diff too large to display
Diff for app-route-ex..ntime.dev.js
Diff too large to display
Diff for app-route-ex..time.prod.js
Diff too large to display
Diff for app-route-tu..time.prod.js
Diff too large to display
Diff for app-route-tu..time.prod.js
Diff too large to display
Diff for app-route.runtime.dev.js
Diff too large to display
Diff for app-route.ru..time.prod.js
Diff too large to display
e95d127
to
61ba6a6
Compare
61ba6a6
to
7cccab8
Compare
@ztanner is this one stale? 😞 |
Still very much planned! Just being fleshed out a bit more with other things that we’re working on :) |
3df1ce1
to
4b7b241
Compare
df2bca0
to
32957e5
Compare
32957e5
to
ee411bc
Compare
947da6a
to
96ada8c
Compare
96ada8c
to
481684d
Compare
481684d
to
38766b5
Compare
Merge activity
|
@ztanner I just did a few first experiments with With different root layouts being possible, I found it really cool that you can also create Note that I've only used it in combination with stable rendering modes from Next.js so far, no One thing I noticed though is the handling of a global If you use If you add If you add Adding Intuitively, I'd think Maybe that's something to consider? Not sure if I'm missing something. Here's a reproduction if you're interested. Anyway, I'm incredibly happy that |
…d for overriding the locale (#1625) **tldr;** — Do you use i18n routing and have you already switched to [`await requestLocale`](https://next-intl.dev/blog/next-intl-3-22#await-request-locale) in `getRequestConfig`? If yes, you can skip this. --- ### Deprecation of `locale` in favor of `await requestLocale` In [next-intl 3.22](https://next-intl.dev/blog/next-intl-3-22), the `locale` argument that was passed to `getRequestConfig` was deprecated in favor of [`await requestLocale`](https://next-intl.dev/blog/next-intl-3-22#await-request-locale): ```diff // i18n/request.ts export default function getRequestConfig(async ({ - locale + requestLocale }) => { + const locale = await requestLocale; // ... })); ``` This change was done in preparation for Next.js 15 where reading from headers [became async](https://nextjs.org/blog/next-15#async-request-apis-breaking-change). If you're using i18n routing, please upgrade to `requestLocale` now. ### Preview: `rootParams` are coming to Next.js Now, with [`rootParams`](vercel/next.js#72837) being on the horizon, this API will allow you to read a locale without receiving any param passed to `getRequestConfig`: ```tsx // i18n/request.ts import {unstable_rootParams as rootParams} from 'next/server'; import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing'; export default getRequestConfig(async () => { const params = await rootParams(); const locale = hasLocale(routing.locales, params.locale) ? params.locale : routing.defaultLocale; // ... }); ``` Among other simplifications, this allows to remove manual overrides like this that were merely done for enabling static rendering: ```diff - type Props = { - params: Promise<{locale: string}>; - }; export async function generateMetadata( - {params}: Props ) { - const {locale} = await params; - const t = await getTranslations({locale, namespace: 'HomePage'}); + const t = await getTranslations('HomePage'); // ... } ``` However, in some rare cases, you might want to render messages from multiple locales on the same page: ```tsx // Use messages from 'en', regardless of what the current user locale is const t = getTranslations({locale: 'en'}); ``` If you're using this pattern, you'll be able to accept the overridden locale in `getRequestConfig` as follows: ```tsx // i18n/request.ts import {unstable_rootParams as rootParams} from 'next/server'; import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing'; export default getRequestConfig(async ({locale}) => { // Use a locale based on these priorities: // 1. An override passed to the function // 2. A locale from the `[locale]` segment // 3. A default locale if (!locale) { const params = await rootParams(); locale = hasLocale(routing.locales, params.locale) ? params.locale : routing.defaultLocale; } // ... }); ``` This is quite an edge case, but this use case will remain supported via the re-introduced `locale` argument. Note that `await requestLocale` considers a potential locale override, therefore the `locale` argument will only be relevant once `rootParams` are a thing. I hope to have more to share on this in the future!
@ztanner I had a bit more time to test and I think I found another bug. If you have an app with a top-level Reproduction:
Note that with |
Thanks for the early feedback @amannn! We'll be taking a look at these as we work to stabilize the feature. |
Sure, hope you didn’t mind the unsolicited feedback! I’m really excited for |
What & Why
It's common to have top-level "dynamic" params that remain constant (as "global" or "root" params) regardless of where you are within a root layout. For example,
[lang]
or[locale]
, regardless of where you are in that layout that param will be available. We should provide a more convenient way to access these params without requiring it to be plumbed through ALS or context.How
This introduces a new API,
unstable_rootParams
, that will return all segment params up to and including the root layout. In other words:/app/[foo]/[bar]/layout.tsx
->{ foo: string, bar: string }
/app/[foo]/[bar]/[baz]/page.tsx
->{ foo: string, bar: string }
(baz
is not included here since the root params up to the root layout were justfoo
&bar
.This also supports the case of having multiple root layouts. Since navigating between a root layouts will trigger an MPA navigation, we're still able to enforce that those params will not change.
This PR also includes some work to the types plugin generate types for root params because they can be statically determined ahead of time. For example, in the above example,
unstable_rootParams()
will be typed asPromise<{ foo: string, bar: string }>
.In the case where there are multiple root layouts, it gets a bit more nuanced. e.g. at build time we aren't able to determine if you're going to be accessing rootParams on root layout A or root layout B. For this reason, they'll become optionally typed, eg:
Promise<{ foo?: string }>
wherefoo
might only be available on the/app/(marketing)/[foo]/layout.tsx
root.This feature is experimental and under active development and as such, is currently exported with an
unstable
prefix.