vercel / next.js

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

Next.js removes css modules causing unstyled pages in production #68328

Open jantimon opened 1 month ago

jantimon commented 1 month ago

Link to the code that reproduces this issue

https://codesandbox.io/p/devbox/gracious-keller-qdj5ld?file=%2Fpages%2Findex.tsx%3A1%2C29

To Reproduce

  1. Open a page that shows a component which is normally loaded and lazy loaded at the same time.
  2. Use the pages router to do a client-side navigation to a page where the component is only lazy loaded.
  3. The CSS is missing and the lazy loaded component is shown unstyled

Reproduction steps using the provided CodeSandbox:

  1. Open the reproduction: https://codesandbox.io/p/devbox/gracious-keller-qdj5ld
  2. Observe that the lazy-loaded Star component on the home page is styled styled star
  3. Navigate to the about page (/about-us)
  4. Reload
  5. Click on the "Back to home" link to navigate to /
  6. Observe that the lazy-loaded Star component on the home page is unstyled unstyled star

Video:

https://github.com/user-attachments/assets/2d528874-d1fd-4389-b4c1-1332ef74fa3d

Current vs. Expected behavior

Current behavior:

When navigating from a page with both normally loaded and lazy-loaded components to a page with only lazy-loaded components, the CSS for the lazy-loaded component is missing.

Expected behavior:

The CSS for lazy-loaded components should be properly applied regardless of the navigation path or the loading method on the previous page.

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Sun Aug  6 20:05:33 UTC 2023
  Available memory (MB): 4102
  Available CPU cores: 2
Binaries:
  Node: 20.9.0
  npm: 9.8.1
  Yarn: 1.22.19
  pnpm: 8.10.2
Relevant Packages:
  next: 15.0.0-canary.91 // Latest available version is detected (15.0.0-canary.91).
  eslint-config-next: N/A
  react: 19.0.0-rc.0
  react-dom: 19.0.0-rc.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

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

Lazy Loading, Pages Router

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

next build (local), Vercel (Deployed)

Additional context

icyJoseph commented 1 month ago

A reproduction repo on Stackblitz, https://stackblitz.com/edit/nextjs-6mb95w?file=pages%2Fabout.tsx,pages%2Findex.tsx,components%2Flazy.tsx, just cancel the command, and run, npm i && npx next build && npx next start

Removed a bunch of code not needed to reproduce the issue, and included next/dynamic

icyJoseph commented 1 month ago

So, the issue seems to, at least, be originating here, https://github.com/vercel/next.js/blob/cab4b653997b83ec3b80bb2f0138d59e5d9e8f5c/packages/next/src/client/index.tsx#L682-L697

Specifically here:

styleTag.setAttribute('media', 'x')
Screenshot 2024-07-31 at 09 05 55

Changing to media="all", brings back the styles, as expected... so the question is why does it believe that this href doesn't apply... 🤔

samcx commented 1 month ago

@jantimon Thank you for submitting an issue!

Is there a reason why you are not using next/dynamic for lazy loading components → https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#nextdynamic?

icyJoseph commented 1 month ago

@jantimon Thank you for submitting an issue!

Is there a reason why you are not using next/dynamic for lazy loading components → https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#nextdynamic?

The Stackblitz example I made does use next/dynamic though. Same problem.

samcx commented 1 month ago

@icyJoseph @jantimon It looks like this is related to this long-running issue → https://github.com/vercel/next.js/issues/33286.

Good news, we've actually started picking this up! → https://github.com/vercel/next.js/pull/68396

jantimon commented 1 month ago

@samcx - Good news

really really cool!

@samcx - why no next/dynamic:

next/dynamic works only for react components and adds some (tiny) overhead for client only components - but it actually uses the same import() syntax to load the dynamic piece under the hood so it shouldn't matter

@icyJoseph - styleTag.setAttribute('media', 'x')

There are two different systems running: the extract text plugin which adds code to lazy loads the css chunk and the nextjs router which adds/removes styles to keep the correct order and tries to apply only relevant styles for the current page - the problem seems to be that the mini-css-extract-plugin adds the lazy css only once but nextjs will remove it every time you go to another page