After building a Next.js project it will provide you with a nice overview of all the pages it built. At times you might look surprised to see some pages marked as dynamic. Next.js contains a number of small implicit rules that determine whether a page is dynamic or not. To save you and me some time here they are (as of Next.js version 13 and 14):
It's possible to define parameters in route paths, such as
[id]
in a route named /post/[id]
. Because Next.js
cannot figure out all the possible values of id
it will
dynamically render the page when a request comes in. To avoid this you can
define a generateStaticParams
function and export it from the
page. This function needs to return an array of all the possible values of
id
.
But just using generateStaticParams
is not entirely enough.
Next.js might encounter a value being passed that was not found in the array
while the server is running and attempt to render it at runtime. You can force
it to accept only the values that you supply by exporting a variable named
dynamicParams
and setting it to false
.
// This is optimal export const dynamicParams = false export async function generateStaticParams() { return [ {id: 'a'}, {id: 'b'} ] }
The second argument of a page server component contains the search params from the url. Simply accessing it triggers the dynamic flag.
Using the useSearchParams
hook in a client side component without
a Suspense
boundary triggers dynamic.
Accessing cookies()
or headers()
inside a Server
component triggers dynamic.
import {cookies} from 'next/headers' cookies().get('test') // This line triggers dynamic
Using a value of 'no-store'
or 'no-cache'
in the
options you pass to the fetch()
function will trigger dynamic.
Using a value of {next: {revalidate: 0}}
will also trigger
dynamic.
await fetch('https://example.com', { cache: 'no-store' // This line triggers dynamic })
A special mention to draftMode
which does
not trigger dynamic. You can safely call and check the
isEnabled
flag and your page will still be built statically.
import {draftMode} from 'next/headers' draftMode().isEnabled // This is fine
If your page shows up as static but you want to force data to be fetched
whenever a request comes you can export the dynamic
setting with
a value of 'force-dynamic'
.
export const dynamic = 'force-dynamic'
If you want to be sure a page is only ever rendered statically you can add the
dynamic
setting with a value of 'error'
. This will
provide you with a warning at build time if any of the above conditions are
met.
export const dynamic = 'error'