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'