Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jantimon opened this issue Jul 30, 2024 · 9 comments · May be fixed by #68396
Open

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

jantimon opened this issue Jul 30, 2024 · 9 comments · May be fixed by #68396
Labels
bug Issue was opened via the bug report template. Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). linear: next Confirmed issue that is tracked by the Next.js team. Pages Router Related to Pages Router.

Comments

@jantimon
Copy link
Contributor

jantimon commented Jul 30, 2024

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:

CSSModules.mp4

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

  • I could reproduce the issue in Next.js versions:
    • next 12.3.4
    • next 13.0.0
    • next 14.0.0
    • next 14.1.3
    • next 14.2.3
    • next 15.0.0-canary.91
  • It happens only for the pages router - the app router seems to work fine
  • Happens only for npm run build - npm run dev seems to work fine
  • The issue seems to be related to the client-side rendering logic in packages/next/src/client/index.tsx
  • This bug cause significant styling issues in production environments
@jantimon jantimon added the bug Issue was opened via the bug report template. label Jul 30, 2024
@github-actions github-actions bot added Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). Pages Router Related to Pages Router. labels Jul 30, 2024
@icyJoseph
Copy link
Contributor

icyJoseph commented Jul 30, 2024

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
Copy link
Contributor

So, the issue seems to, at least, be originating here,

styleSheets.forEach(({ href, text }: { href: string; text: any }) => {
if (!currentHrefs.has(href)) {
const styleTag = document.createElement('style')
styleTag.setAttribute('data-n-href', href)
styleTag.setAttribute('media', 'x')
if (nonce) {
styleTag.setAttribute('nonce', nonce)
}
document.head.appendChild(styleTag)
styleTag.appendChild(document.createTextNode(text))
}
})
return true
}

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
Copy link
Member

samcx commented Aug 5, 2024

@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
Copy link
Contributor

icyJoseph commented Aug 6, 2024

@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
Copy link
Member

samcx commented Aug 6, 2024

@icyJoseph @jantimon It looks like this is related to this long-running issue → #33286.

Good news, we've actually started picking this up! → #68396

@jantimon
Copy link
Contributor Author

jantimon commented Aug 6, 2024

@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

@github-actions github-actions bot added the linear: next Confirmed issue that is tracked by the Next.js team. label Aug 12, 2024
samcx added a commit that referenced this issue Aug 12, 2024
## Why?

To help flag severe issues, we can use utilize AI to digest the
`context.payload.issue` object each time an issue is opened and
determine whether that issue is severe enough to flag to the Next.js
team on Slack.

## How?

Making a Tool Call using using the :ai-stonks: SDK. The :ai-stonks: will call the Tool and
report to Slack (to the `#next-info` channel`) if it determines that the
issue is severe enough, using the context of the issue, latest Next.js
versions, and a Triage Guideline.

## Testing

This was tested on a separate test repo with issues
#68328 (real issue) and
#68336 (blank test issue).

![CleanShot 2024-07-30 at 12 07
43@2x](https://github.com/user-attachments/assets/7a35514e-840d-41db-b1a7-6021fe389458)

![CleanShot 2024-07-30 at 12 10
26@2x](https://github.com/user-attachments/assets/c7e17612-59b6-4ce3-8b7f-ea96c3e09680)
@max-degterev
Copy link

I am getting the same issue now with app router, some css modules simply not loading for certain routes. Happens on both dev and prod

@jantimon
Copy link
Contributor Author

@max-degterev that is probably another bug - can you please create a dedicated issue?

@max-degterev
Copy link

@jantimon

Aha I've managed to trace the error.

Output v14.2.1:
Screenshot 2024-09-28 at 10 00 02 PM

Output v14.2.13:
Screenshot 2024-09-28 at 9 58 55 PM

There is a rogue class with a semicolon after the closing curly. Because nextjs doesn't guarantee class order any class that follows that rogue definition will not apply. This is a floating bug that will be very hard to trace. Ideally compiler should throw or strip these.

This little semicolon just cost me 4 hours.

I'm not gonna create a separate issue for this because frankly your issue creation rules are not very inviting to collaborate. I will patch this in my codebase, the rest is up to you guys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template. Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). linear: next Confirmed issue that is tracked by the Next.js team. Pages Router Related to Pages Router.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants