vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.43k stars 26.79k forks source link

bug: unable to render static pages if headers used in root layout even if promisified #60009

Open juliusmarminge opened 9 months ago

juliusmarminge commented 9 months ago

Link to the code that reproduces this issue

https://github.com/juliusmarminge/nextjs-static-promise-context/tree/static-render

To Reproduce

  1. Clone repo: gh repo clone juliusmarminge/nextjs-static-promise-context
  2. Checkout static-render branch: git checkout static-render
  3. Build: pnpm build
    • You'll see that no pages were rendered statically.
  4. Comment out app/layout.tsx:43, meaning we'll just send some mock headers
  5. Build again: pnpm build
    • You'll see that pages are able to be statically rendered since we're no longer invoking headers()

Current vs. Expected behavior

@sebmarkbage mentioned on X we should be able to pass the dynamic function call down the tree as a promise: https://x.com/sebmarkbage/status/1722794007527358531?s=20

However I am unable to get this working while retaining the ability to render some pages statically...

The behavior I am expecting, or at least want should happen, is that /static be statically rendered since that route makes no requests and thus never "consumes" the headers promise. /dynamic should be a server-rendered page as it does make a request and needs to await the promise.

I might have mis-interpreted what Sebastian meant in his tweet though, but I assumed this was the way to go. How else would we be able to access headers in client components during SSR?

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:18 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6000
Binaries:
  Node: 20.10.0
  npm: 10.2.3
  Yarn: 1.22.19
  pnpm: 8.6.2
Relevant Packages:
  next: 14.0.5-canary.29
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

App Router

Additional context

No response

NEXT-2855

Bahlaouane-Hamza commented 9 months ago

This is what i got when running build

> static-reproo@0.1.0 build /Users/user/dev/next-bugs/nextjs-static-promise-context
> next build

   ▲ Next.js 14.0.5-canary.29

 ✓ Creating an optimized production build
 ✓ Compiled successfully
 ✓ Linting and checking validity of types
 ✓ Collecting page data
   Generating static pages (0/6)  [=   ] << query  #1 app.info  {
  input: undefined,
  result: l [TRPCClientError]: fetch failed
      at l.from (/Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:108717)
      at /Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:112527
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
    meta: undefined,
    shape: undefined,
    data: undefined,
    [cause]: TypeError: fetch failed
        at Object.fetch (node:internal/deps/undici/undici:11372:11)
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
      cause: [Error]
    }
  },
  elapsedMs: 49
}
l [TRPCClientError]: fetch failed
    at l.from (/Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:108717)
    at /Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:112527
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  meta: undefined,
  shape: undefined,
  data: undefined,
  [cause]: TypeError: fetch failed
      at Object.fetch (node:internal/deps/undici/undici:11372:11)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
    cause: Error: connect ECONNREFUSED ::1:3000
        at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
        at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
      errno: -61,
      code: 'ECONNREFUSED',
      syscall: 'connect',
      address: '::1',
      port: 3000
    }
  }
}

Error occurred prerendering page "/dynamic". Read more: https://nextjs.org/docs/messages/prerender-error
TRPCClientError: fetch failed
    at l.from (/Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:108717)
    at /Users/user/dev/next-bugs/nextjs-static-promise-context/.next/server/chunks/582.js:1:112527
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
 ✓ Generating static pages (6/6)

> Export encountered errors on following paths:
    /dynamic/page: /dynamic
 ELIFECYCLE  Command failed with exit code 1.

I'm not using ARM but x64

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 22.6.0: Wed Oct  4 21:25:26 PDT 2023; root:xnu-8796.141.3.701.17~4/RELEASE_X86_64
Binaries:
  Node: 18.18.2
  npm: 9.8.1
  Yarn: N/A
  pnpm: 8.9.0
Relevant Packages:
  next: 14.0.5-canary.29
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

I had to add export const dynamic = 'force-dynamic' to app/dynamic/page.tsx to build it

~/dev/next-bugs/nextjs-static-promise-context on static-render !1 ❯ pnpm build                                                                                           took 11s

> static-reproo@0.1.0 build /Users/user/dev/next-bugs/nextjs-static-promise-context
> next build

   ▲ Next.js 14.0.5-canary.29

 ✓ Creating an optimized production build
 ✓ Compiled successfully
 ✓ Linting and checking validity of types
 ✓ Collecting page data
 ✓ Generating static pages (6/6)
 ✓ Collecting build traces
 ✓ Finalizing page optimization

Route (app)                              Size     First Load JS
┌ ○ /_not-found                          886 B          84.5 kB
├ λ /api/trpc/[trpc]                     0 B                0 B
├ λ /dynamic                             773 B           110 kB
└ ○ /static                              344 B          88.9 kB
+ First Load JS shared by all            83.6 kB
  ├ chunks/772-b131746d7a2113d2.js       28.4 kB
  ├ chunks/a7fb4cfa-aacc8b7c58ba061c.js  53.3 kB
  ├ chunks/main-app-420d7659b6aab25c.js  218 B
  └ chunks/webpack-4d0c252919c3ddf8.js   1.74 kB

○  (Static)   prerendered as static content
λ  (Dynamic)  server-rendered on demand using Node.js
juliusmarminge commented 9 months ago

@Bahlaouane-Hamza Is adding export const dynamic = "force-dynamic" all you did? You got the correct build output where /static and /_not-found are static 👀

EDIT: Oh you added it to dynamic page... hmm

juliusmarminge commented 9 months ago

Either way there has to be a way to forward headers to client components during SSR without making everything dynamic, right? I'd love to hear from the team how we're intended to do this.

Bahlaouane-Hamza commented 9 months ago

@Bahlaouane-Hamza Is adding export const dynamic = "force-dynamic" all you did? You got the correct build output where /static and /_not-found are static 👀

EDIT: Oh you added it to dynamic page... hmm

Yes, that's all i had to do, i've seen a lot of quirk issues related to ARM.

statusunknown418 commented 8 months ago

any updates here?

Edit by maintainers: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!

samcx commented 6 months ago

@juliusmarminge Thanks for sharing!

This is the expected behavior when you use headers() in Layouts → https://nextjs.org/docs/app/api-reference/functions/headers#headers—it opts the entire route into dynamic, so Layouts and children below.

PPR will be able to help here (stops from opting an entire route to dynamic when something dynamic like headers() is used.