Dynamic Routes in App Router are considered Server Functions
Verify canary release
- [X] I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.6.0: Wed Jul 5 22:22:05 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T6000
Binaries:
Node: 20.5.0
npm: 9.8.0
Yarn: 1.22.19
pnpm: N/A
Relevant Packages:
next: 13.4.12
eslint-config-next: 13.4.12
react: 18.2.0
react-dom: 18.2.0
typescript: 5.1.6
Next.js Config:
output: N/A
Which area(s) of Next.js are affected? (leave empty if unsure)
App Router, Data fetching (gS(S)P, getInitialProps)
Link to the code that reproduces this issue or a replay of the bug
https://codesandbox.io/p/sandbox/nextjs-dynamic-route-bug-report-scwrgr
To Reproduce
Step 1: Start with an empty app directory Next project.
Step 2: Add /app/test/[slug]/page.tsx:
const Test = ({ params: { slug } }: { params: { slug: string } }) => {
return <div>Slug: {slug}</div>;
};
export default Test;
Step 3: Run next build
Observe that the Route is considered a Server route:
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ λ /test/[slug] 145 B 78.4 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
Describe the Bug
The route /test/[slug] is considered a Server route despite the fact that it uses no dynamic functions. It does not seem to be cached via the Full Route Cache and causes a function execution on every load.
Expected Behavior
I expected the route /test/[slug] to be a Static route and be cached (ISR / Full Route Cache)
You can work around this issue by setting the following flags, which causes NextJS to consider the route Static:
export const dynamic = "error";
export const dynamicParams = true;
I expected the default behavior to be Static and not Server.
Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
Vercel
Hello, The thing is that next cannot know which [slug] to generate at build time, you didn't provide it with a fixed set of paths to generate.
So it assumes you want to generate the page at runtime.
If you want to generate at build time use generateStaticParams :
https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#generating-static-params
I did a couple of tests with regards to dynamic, dynamicParams and generateStaticParams.
The route being tested is:
/app/test/[slug].ts
const Test = ({ params: { slug } }: { params: { slug: string } }) => {
return <div>Slug: {slug}</div>;
};
export default Test;
No segment config
Result: /test/[slug] is considered Server
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ λ /test/[slug] 145 B 78.4 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
dynamic & dynamicParams
Result: /test/[slug] is considered Static
export const dynamic = "error";
export const dynamicParams = true;
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ ○ /test/[slug] 145 B 78.4 kB
○ (Static) automatically rendered as static HTML (uses no initial props)
Empty generateStaticParams
Result: /test/[slug] is considered SSG
export const generateStaticParams = () => [];
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ ● /test/[slug] 145 B 78.4 kB
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
dynamic & dynamicParams & empty generateStaticParams
Result: /test/[slug] is considered Static
export const dynamic = "error";
export const dynamicParams = true;
export const generateStaticParams = () => [];
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ ○ /test/[slug] 145 B 78.4 kB
○ (Static) automatically rendered as static HTML (uses no initial props)
dynamic & dynamicParams and non-empty generateStaticParams
Result: /test/[slug] is considered SSG
export const dynamic = "error";
export const dynamicParams = true;
export const generateStaticParams = () => [{ slug: 'a' }];
Route (app) Size First Load JS
┌ ○ / 145 B 78.4 kB
└ ● /test/[slug] 145 B 78.4 kB
└ /test/a
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
When no route config is specified, the default behavior of NextJS seems to be to not cache the route at all. I find this unexpected because to me it seems like NextJS could easily optimize the route.
I can fix the problem by specifying route config:
- Set dynamic = "error" and dynamicParams = true => route is considered "Static"
- Provide a generateStaticParams() function which returns an empty array => route is considered "SSG"
I am confused about the difference between "SSG" and "Static" in this case. I do not know which of the two fixes is the desired one...
@Yonom How did you handle 404s for routes that are invalid, ie slug that are invalid? If I use not_found, It still adds the route to cache with the not_found markup as content.
This issue has been automatically marked as stale due to inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you.
This issue has been automatically closed due to inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding!