searchParams object is empty while having dynamic = "force-dynamic" in page
Answered
Russian Toy posted this in #help-forum
Russian ToyOP
Hi,
I enforced a page to be dynamically rendered but i noticed the searchParams is not giving any data on production. The type is from the doc : searchParams: Promise<{ [key: string]: string | string[] | undefined }>.
I noticed also I have the searchParams data in the generateMetadata function, but not in my async page.... which is weird.
This page is under a dynamic route [lang] (dynamic routing) like this : /[locale]/mypage. This page is not dynamic route juste a regular async page.
Anyone that also had this problem / could help with ?
I really need this page to process SearchParams before rendering (security purposes) . I use next 15.3.5
I enforced a page to be dynamically rendered but i noticed the searchParams is not giving any data on production. The type is from the doc : searchParams: Promise<{ [key: string]: string | string[] | undefined }>.
I noticed also I have the searchParams data in the generateMetadata function, but not in my async page.... which is weird.
This page is under a dynamic route [lang] (dynamic routing) like this : /[locale]/mypage. This page is not dynamic route juste a regular async page.
Anyone that also had this problem / could help with ?
I really need this page to process SearchParams before rendering (security purposes) . I use next 15.3.5
Answered by alfonsüs ardani
I mean you can also try doing it like this
/[locale]/[motherPageSlug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/draftMode/page.tsx
/[locale]/order/cancel/page.tsx
/[locale]/order/processing/page.tsx
/[locale]/order/success/page.tsx
but do NOT put
use
-
-
-
and do not use
/[locale]/[motherPageSlug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/draftMode/page.tsx
/[locale]/order/cancel/page.tsx
/[locale]/order/processing/page.tsx
/[locale]/order/success/page.tsx
but do NOT put
await draftMode
in /[locale]/layout.tsx
.use
generateStaticParams
in:-
/[locale]/layout.tsx
-
/[locale]/[motherPageSlug]/layout.tsx
-
/[locale]/[motherPageSlug]/[slug]/layout.tsx
and do not use
force-static
and force-dynamic
123 Replies
@Russian Toy Hi,
I enforced a page to be dynamically rendered but i noticed the searchParams is not giving any data on production. The type is from the doc : searchParams: Promise<{ [key: string]: string | string[] | undefined }>.
I noticed also I have the searchParams data in the generateMetadata function, but not in my async page.... which is weird.
This page is under a dynamic route [lang] (dynamic routing) like this : /[locale]/mypage. This page is not dynamic route juste a regular async page.
Anyone that also had this problem / could help with ?
I really need this page to process SearchParams before rendering (security purposes) . I use next 15.3.5
the result on production should be the same when you do
can you also give me the page.tsx?
next build
. is the bug reproducible in next build
?can you also give me the page.tsx?
This page is under a dynamic route [lang] (dynamic routing) like this : /[locale]/mypage. This page is not dynamic route juste a regular async page.This is confusing. you said its not dynamic route but it has [locale] and [lang] and
dynamic="force-dynamic"
.Russian ToyOP
Hi, thank you for your help,
Here is the next build :
Here is the next build :
Route (app) Size First Load JS
┌ ○ /_not-found 977 B 102 kB
├ ○ /[locale] 409 B 150 kB
├ ○ /[locale]/[motherPageSlug] 518 B 150 kB
├ ○ /[locale]/[motherPageSlug]/[slug] 518 B 150 kB
├ ƒ /[locale]/order/cancel 329 B 115 kB
├ ƒ /[locale]/order/processing 328 B 115 kB
├ ƒ /[locale]/order/success 331 B 115 kB
├ ƒ /[locale]/sitemap.xml 147 B 101 kB
├ ƒ /api/exit-preview 147 B 101 kB
├ ƒ /api/orders/checkout 237 B 107 kB
├ ƒ /api/orders/payment/verify 237 B 107 kB
├ ƒ /api/preview 237 B 107 kB
├ ƒ /api/revalidate 237 B 107 kB
├ ○ /robots.txt 147 B 101 kB
└ ○ /sitemap.xml 147 B 101 kB
+ First Load JS shared by all 101 kB
├ chunks/4bd1b696-8dd6532907bf1f42.js 53.2 kB
├ chunks/684-78be455ebeb65286.js 46.1 kB
└ other shared chunks (total) 1.98 kB
ƒ Middleware 49.1 kB
○ (Static) prerendered as static content
ƒ (Dynamic) server-rendered on demand
@alfonsüs ardani the result on production should be the same when you do `next build`. is the bug reproducible in `next build`?
can you also give me the page.tsx?
> This page is under a dynamic route [lang] (dynamic routing) like this : /[locale]/mypage. This page is not dynamic route juste a regular async page.
This is confusing. you said its not dynamic route but it has [locale] and [lang] and `dynamic="force-dynamic"`.
Russian ToyOP
the page is /[locale]/order/success, so this page is not dynamic routing itself
@Russian Toy Click to see attachment
remove this part:
console.log(` searchParams : ${JSON.stringify({ searchParams })}`);
and try again
you printed
the promise
not the searchParams object....
Russian ToyOP
console log from metadata does not change anything to the problem here, the logs from page show empty object :
thats weird 

Russian ToyOP
yes 😿
try
console.log("SearchParams: ${ await searchParams }")
Russian ToyOP
still empty object
try moving this
const translate = await getTranslations("Order");
below all the console.logsRussian ToyOP
ok i will try
what the helly
time to troubleshoot and comment half of the code to see if its still empty
Russian ToyOP
i noticed i have a log saying this. :
pending revalidates promise finished for: {
pathname: '/fr/order/success',
query: undefined,
search: '?sess=1234&ord=abcd',
hash: '
href: '/fr/order/success?sess=abcd&ord=1234',
slashes: undefined
in the order, i have the log from the page (empty), the log from generateMetadata (full), and this one above
this is messy 😅
where is this?
pending revalidates promise finished for
i didnt find it in the codeRussian ToyOP
it is logged in the terminal
from nextjs

Russian ToyOP
(i just upgraded to 15.5, to try if it was a bug that's been fixed, maybe that's why)
Russian ToyOP
i just find why: the rootlayout was using force-static, i removed it it works. But i need it for ISR purpose (& cost lol). My app is perfectly working other than that
@Russian Toy i just find why: the rootlayout was using force-static, i removed it it works. But i need it for ISR purpose (& cost lol). My app is perfectly working other than that
routes are "computed" as a whole so u can't just make rootlayout static unles you use ppr
best to determine static/dynamic directly in the
page.tsx
if you are not using pprand keep layout UI only... with little to no data fetches.. and no auth check
Russian ToyOP
i have a dynamic layout (using ISR in my layout) & i use draftmode here
i need it for Header / footer data
@Russian Toy i need it for Header / footer data
You can do that with parallel routes. Leaving the layout without any data fetching.
U cant just "ISR" a layout while leaving child pages SSR'd.
U cant just "ISR" a layout while leaving child pages SSR'd.
The decision to ISR ir SSG or SSR is computed to the whole route as a whole. Not individual segments.
If your layout is ISR but then its child is SSR, it will be confusing to maintain since its not clear if it will be ISR or SSR.
For example your /locale/order/cancel is SSR (denoted by the f symbol in your build log), including the root layout, layout of locale, layout of order and layout of cancel.
If your layout is ISR but then its child is SSR, it will be confusing to maintain since its not clear if it will be ISR or SSR.
For example your /locale/order/cancel is SSR (denoted by the f symbol in your build log), including the root layout, layout of locale, layout of order and layout of cancel.
Russian ToyOP
in my case, i don't see how i can add parallel routes here ? You mean to remove all providers & dynamic in parallel routes ?
export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale?: string }>;
}) {
const { isEnabled = false } = await draftMode();
const { locale } = await params;
if (!locale || !isLocaleType(locale)) {
notFound();
}
return (
<html lang={locale} className={lato_font.variable}>
<body>
<NextIntlClientProvider>
<ClientProviders>
<LayoutWithDynamicZone isDraft={isEnabled} locale={locale}>
{children}
</LayoutWithDynamicZone>
</ClientProviders>
</NextIntlClientProvider>
</body>
</html>
);
}
@Russian Toy in my case, i don't see how i can add parallel routes here ? You mean to remove all providers & dynamic in parallel routes ?
`export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale?: string }>;
}) {
const { isEnabled = false } = await draftMode();
const { locale } = await params;
if (!locale || !isLocaleType(locale)) {
notFound();
}
return (
<html lang={locale} className={lato_font.variable}>
<body>
<NextIntlClientProvider>
<ClientProviders>
<LayoutWithDynamicZone isDraft={isEnabled} locale={locale}>
{children}
</LayoutWithDynamicZone>
</ClientProviders>
</NextIntlClientProvider>
</body>
</html>
);
}`
Thats the problem with using [locale] as localization. Im not well versed in how to optimally add i18n in nextjs but if you do
Im not sure if ur i18n library can support this behavior.
await param
in root layout, it will make itself and all child segment dynamic. One way to mitigate this is to pre-generate the param
using generateStaticParams
.Im not sure if ur i18n library can support this behavior.
The website literally doesnt know anything about param at build time so how can it know if its not found or not? 

Forcing it to be static would just makes it weird and hard to debug
Draftmode uses headers so it will also convert static pages to dynamic SSR.
Do you really need draft mode at the root layout?
Do you really need draft mode at the root layout?
Russian ToyOP
I use next-intl for this purpose 🙂
@alfonsüs ardani The website literally doesnt know anything about param at build time so how can it know if its not found or not? <:ThinkEyes:711813052433039370>
Russian ToyOP
i had generateStaticParams in layout, but revalidatePath for child ISR was not working
I havent used next-intl so i cant help with that sorry.
But it is well known fact that next cant access param in build time since... there is no user...
But it is well known fact that next cant access param in build time since... there is no user...
@alfonsüs ardani Draftmode uses headers so it will also convert static pages to dynamic SSR.
Do you really need draft mode at the root layout?
Russian ToyOP
only to get draft data from header / footer
@Russian Toy i had generateStaticParams in layout, but revalidatePath for child ISR was not working
Its because your child is all SSR. RevalidatePath doesnt affect caching of an SSR page. But they DO affect cached data inside an SSR page.
Cached data = data returned by fetch() and unstable_cach()
They dont revalidate SSR page since the page will always be freshly made per request
Russian ToyOP
i did use 'force-static' in my dynamic route and use revalidateTag("my-fetch-tag-on-dynamic-route")
but maybe i don't really understand how it works here
Dont use force static in dynamic route :/
It clearly breaks all your code here.
If you want dynamic page to be static you need
It clearly breaks all your code here.
If you want dynamic page to be static you need
generateStaticParam
Russian ToyOP
it was the only way i found where i can make it work for static build page and get draftmode working and get updated content with revalidateTag :/
Russian ToyOP
sorry, bad english, i wanted to say i wanted to implement ISR so i have updated content without rebuilding all website
then you put ISR here:
├ ○ /[locale]/[motherPageSlug]/page.tsx
├ ○ /[locale]/[motherPageSlug]/[slug]/page.tsx
not at
├ ○ /[locale]/[motherPageSlug]/page.tsx
├ ○ /[locale]/[motherPageSlug]/[slug]/page.tsx
not at
/[locale]/layout.tsx
use
generateStaticParam
to generate dynamic routes so that it can be used statically. dont use force-static
Russian ToyOP
but how do i do if i have content that need to be revalidated on layout => header/footer
then dont put header/footer on
put it on parallel routes like
and
or just put both header/footer directly in
or
but NOT
/app/[locale]/layout.tsx
put it on parallel routes like
/app/[locale]/@header/[motherPageSlug]/page.tsx
and
/app/[locale]/@footer/[motherPageSlug]/page.tsx
or just put both header/footer directly in
/[locale]/[motherPageSlug]/page.tsx
or
/[locale]/[motherPageSlug]/layout.tsx
but NOT
/app/[locale]/layout.tsx
Russian ToyOP
and what is the less expensive parallel routes or duplicating header/footer in layout where i need it ?
oh
i understand
you can still use
layout.tsx
in parallel routeslike this
/app/[locale]/@header/[motherPageSlug]/layout.tsx
/app/[locale]/@header/[motherPageSlug]/page.tsx
/app/[locale]/@header/[motherPageSlug]/layout.tsx
/app/[locale]/@header/[motherPageSlug]/page.tsx
parallel routes is slightly less expensive in terms of size for each request
but its negligible if you dont have that much client component
why not put header/footer here?
/[locale]/[motherPageSlug]/layout.tsx
@alfonsüs ardani why not put header/footer here?
`/[locale]/[motherPageSlug]/layout.tsx`
Russian ToyOP
i still need it for order routes (cancel, succes, etc)
but they are different header/footer no?
Russian ToyOP
no they are common, it is just the same data coming from headless cms
try separating the routes via route groups then just to be clear about which one is ISR and which one is SSR
like
/(static)/[locale]/[motherPageSlug]/[slug]/page.tsx
/(static)/[locale]/[motherPageSlug]/[slug]/page.tsx
/(dynamic)/[locale]/order/cancel/page.tsx
/(dynamic)/[locale]/order/processing/page.tsx
/(dynamic)/[locale]/order/success/page.tsx
like
/(static)/[locale]/[motherPageSlug]/[slug]/page.tsx
/(static)/[locale]/[motherPageSlug]/[slug]/page.tsx
/(dynamic)/[locale]/order/cancel/page.tsx
/(dynamic)/[locale]/order/processing/page.tsx
/(dynamic)/[locale]/order/success/page.tsx
that way you can put your ISR + revalidatePath on the /(static) folder
then also have the SSR of orders and consume
then also have the SSR of orders and consume
params
consume cookies
headers
and even draftmode
in /(dynamic)Russian ToyOP
you mean :
/[locale]/(static)/[motherPageSlug]/[slug]/page.tsx
/[locale]/(dynamic)/order/...
/[locale]/(static)/[motherPageSlug]/[slug]/page.tsx
/[locale]/(dynamic)/order/...
@Russian Toy you mean :
/[locale]/(static)/[motherPageSlug]/[slug]/page.tsx
/[locale]/(dynamic)/order/...
no, /(static)/[locale]/layout.tsx and /(dynamic)/[locale]/layout.tsx
you can't mix static logic and dynamic logic together :(((
you can't mix static logic and dynamic logic together :(((
hard to maintain
Russian ToyOP
it will work even if they a common dynamic route (locale) between (dynamic) & (static) ?
yes because you have a page under it.
lets say you access
it will automatically use
and NOT
lets say you access
acme.com/en/order/cancel
it will automatically use
/(dynamic)/[locale]/layout.tsx
and NOT
/(static)/[locale]/layout.tsx
if you access
then it will automatically use
acme.com/en/asdfasdfasdf/asdfasdf
then it will automatically use
/(static)/[locale]/layout.tsx
Russian ToyOP
i dont know if this will work as it seems mandatory for next-intl : https://next-intl.dev/docs/routing/setup#layout
@Russian Toy i dont know if this will work as it seems mandatory for next-intl : https://next-intl.dev/docs/routing/setup#layout
you put
generateStaticParams
in /(static)/[locales]/layout.tsx
@alfonsüs ardani yes because you have a page under it.
lets say you access `acme.com/en/order/cancel`
it will automatically use `/(dynamic)/[locale]/layout.tsx`
and NOT `/(static)/[locale]/layout.tsx`
Russian ToyOP
so i need to duplicate my layout but where do i put rootlayout ?
Russian ToyOP
i need to put the locale as lang in the html tag
<html lang={locale} ...>
// ...
</html>
// ...
</html>
yes put it in
/(static)/[locales]/layout.tsx
and /(dynamic)/[locales]/layout.tsx
Russian ToyOP
but they are conflicting path no ?
Russian ToyOP
ok i am trying it
and regarding pages for /[locale], i should duplicate like this : /(static)/[locales]/page.tsx and /(dynamic)/[locales]/page.tsx ? or only keep it in static ?
@Russian Toy and regarding pages for /[locale], i should duplicate like this : /(static)/[locales]/page.tsx and /(dynamic)/[locales]/page.tsx ? or only keep it in static ?
do you want your www.acme.com/en page static or dynamic?
it static then put it in (static) if its dynamic then put it in (dynamic)
Russian ToyOP
ok ok
they dont have any effect to child path...
only layout.tsx have effect on child path
Russian ToyOP
ok thank you i try it
so like this ?
yes something like that.... it should work based on my experience
Russian ToyOP
ok i could build effectively this, order success page is working, but not the dynamic route pages
@Russian Toy ok i could build effectively this, order success page is working, but not the dynamic route pages
can i see whats wrong with the dynamic route pages?
Russian ToyOP
i have a 500 error on it, sorry i mean i have errors on static/[locale]/[motherPageSlug] and static/[locale]/[motherPageSlug]/[slug]
well what is the error?
Russian ToyOP
if i use draftMode, i can still use draftMode, and generateStaticParams right ?
The error is : ⨯ [Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] {
digest: 'DYNAMIC_SERVER_USAGE'
}
The error is : ⨯ [Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] {
digest: 'DYNAMIC_SERVER_USAGE'
}
here is the new build FYI :
Route (app) Size First Load JS
┌ ƒ /_not-found 383 B 102 kB
├ ● /[locale] 395 B 151 kB
├ ├ /fr
├ └ /en
├ ● /[locale]/[motherPageSlug] 506 B 151 kB
├ ● /[locale]/[motherPageSlug]/[slug] 506 B 151 kB
├ ƒ /[locale]/order/cancel 316 B 116 kB
├ ƒ /[locale]/order/processing 317 B 116 kB
├ ƒ /[locale]/order/success 318 B 116 kB
├ ƒ /api/exit-preview 131 B 102 kB
├ ƒ /api/orders/checkout 227 B 107 kB
├ ƒ /api/orders/payment/verify 227 B 107 kB
├ ƒ /api/preview 227 B 107 kB
├ ƒ /api/revalidate 227 B 107 kB
├ ○ /robots.txt 131 B 102 kB
└ ○ /sitemap.xml 131 B 102 kB
+ First Load JS shared by all 102 kB
├ chunks/255-839588e0f3decf6f.js 45.7 kB
├ chunks/4bd1b696-c023c6e3521b1417.js 54.2 kB
└ other shared chunks (total) 2.01 kB
ƒ Middleware 50 kB
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses generateStaticParams)
ƒ (Dynamic) server-rendered on demand
Route (app) Size First Load JS
┌ ƒ /_not-found 383 B 102 kB
├ ● /[locale] 395 B 151 kB
├ ├ /fr
├ └ /en
├ ● /[locale]/[motherPageSlug] 506 B 151 kB
├ ● /[locale]/[motherPageSlug]/[slug] 506 B 151 kB
├ ƒ /[locale]/order/cancel 316 B 116 kB
├ ƒ /[locale]/order/processing 317 B 116 kB
├ ƒ /[locale]/order/success 318 B 116 kB
├ ƒ /api/exit-preview 131 B 102 kB
├ ƒ /api/orders/checkout 227 B 107 kB
├ ƒ /api/orders/payment/verify 227 B 107 kB
├ ƒ /api/preview 227 B 107 kB
├ ƒ /api/revalidate 227 B 107 kB
├ ○ /robots.txt 131 B 102 kB
└ ○ /sitemap.xml 131 B 102 kB
+ First Load JS shared by all 102 kB
├ chunks/255-839588e0f3decf6f.js 45.7 kB
├ chunks/4bd1b696-c023c6e3521b1417.js 54.2 kB
└ other shared chunks (total) 2.01 kB
ƒ Middleware 50 kB
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses generateStaticParams)
ƒ (Dynamic) server-rendered on demand
no you can't use draftMode :(
I think you have to do something like this if you want draftMode
/(static)/[slug]/[childSlug]/page.tsx
/(dynamic)/[slug]/[childSlug]/draft-mode/page.tsx
and have a dedicated dynamic page for the draft-mode
I think you have to do something like this if you want draftMode
/(static)/[slug]/[childSlug]/page.tsx
/(dynamic)/[slug]/[childSlug]/draft-mode/page.tsx
and have a dedicated dynamic page for the draft-mode
Russian ToyOP
oh i see :/
I mean you can also try doing it like this
/[locale]/[motherPageSlug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/draftMode/page.tsx
/[locale]/order/cancel/page.tsx
/[locale]/order/processing/page.tsx
/[locale]/order/success/page.tsx
but do NOT put
use
-
-
-
and do not use
/[locale]/[motherPageSlug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/page.tsx
/[locale]/[motherPageSlug]/[slug]/draftMode/page.tsx
/[locale]/order/cancel/page.tsx
/[locale]/order/processing/page.tsx
/[locale]/order/success/page.tsx
but do NOT put
await draftMode
in /[locale]/layout.tsx
.use
generateStaticParams
in:-
/[locale]/layout.tsx
-
/[locale]/[motherPageSlug]/layout.tsx
-
/[locale]/[motherPageSlug]/[slug]/layout.tsx
and do not use
force-static
and force-dynamic
Answer
if you put
it will change locale and all its children to be dynamically rendered.
and you can't fix it with force-static..
await draftMode
in /[locale]/layout.tsx
it will change locale and all its children to be dynamically rendered.
and you can't fix it with force-static..
Russian ToyOP
i see !
i was thinking everything could work together
regarding draftmode and generateStaticParams and revalidatePath
yeah
it looks like that at first
but you are mixing static features with dynamic features xD and everything just went boom 🤯
Russian ToyOP
yes ^^
Russian ToyOP
ok i will try it and i will keep you updated, thank you so much, i feel that i understand much better how next js works 🙂
@Russian Toy ok i will try it and i will keep you updated, thank you so much, i feel that i understand much better how next js works 🙂
awesome to hear that! im glad i can level you up in this game
Russian ToyOP
you really help me so much ! thank you and if never you are in Bali, i would happy to offer the first drink !