next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Dynamic Routes in App Router are considered Server Functions

Open Yonom opened this issue 2 years ago • 5 comments

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

Yonom avatar Aug 08 '23 09:08 Yonom

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

Fredkiss3 avatar Aug 08 '23 11:08 Fredkiss3

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)

Yonom avatar Aug 09 '23 11:08 Yonom

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 avatar Aug 09 '23 11:08 Yonom

@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.

shrads12 avatar Jun 12 '24 21:06 shrads12

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.

nextjs-bot avatar Dec 09 '25 23:12 nextjs-bot

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!

nextjs-bot avatar Dec 16 '25 23:12 nextjs-bot