Navigate to http://localhost:3000, observe that the element with "Some text" has styling including background-color and a border.
Click on the "To other page" link below to navigate to http://localhost:3000/client_side_navigation using client-side navigation via next/link.
Observe that the element with "Some text" no longer has any styling associated with it. This component is rendered using the same component from the index page, except wrapped in a next/dynamic call, so in this case we'd expect to see the same background-color and border styles as the index page.
If you reload http://localhost:3000/client_side_navigation you can observe the style it should have
Current vs. Expected behavior
Following the steps from the previous section, I'd expect the /client_side_navigation page to have the same style applied as the / page, since they use the same component and share the same stylesheet.
However, the StyledBoxLazy on the /client_side_navigation page is unstyled when the user does a client-side navigation from / to /client_side_navigation.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 23.2.0: Wed Nov 15 21:55:06 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6020
Binaries:
Node: 18.19.0
npm: 10.2.3
Yarn: 1.22.21
pnpm: 8.14.2
Relevant Packages:
next: 14.1.1-canary.9 // Latest available version is detected (14.1.1-canary.9).
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0
typescript: 5.1.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Other (Deployed)
Additional context
We're running Next via Docker (outside Vercel) but we've been able to reproduce this both in prod and locally. Happy to answer any questions or investigate possible workarounds! From the repro repo I added what seems to be happening and some potential ways to fix it, but I'll include them here too
Explanation
The index page includes a component called StyledBox, which has a dependency on a CSS Module. The client-side-navigation page has a dependency on StyledBoxLazy, which is StyledBox wrapped within next/dyanmic.
The stylesheet is initially included when loading the server-side rendered version of the index page. Because the StyledBox component is not using next/dyanmic on the index page, the stylesheet link tag has a data-n-p attribute.
When the user navigates to client-side-navigation via clicking the Link component, the client-side-navigation page renders StyledBoxLazy, which attempts to load the CSS chunk associated with StyledBoxLazy. In the webpack runtime chunk, mini-css-extract-plugin looks up the file path via the chunk/module id and first checks to see if there is a link tag with a rel=stylesheet and an href that matches the filepath for the stylesheet it needs to load. In this case it finds the stylesheet that was loaded via SSR on the initial index pageload, and does not add an additional link tag for that stylesheet (because one already exists in the document). This happens in this section of the plugin https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/index.js#L924.
After the render phase for client-side-navigation finishes, the onHeadCommit method runs in a useLayoutEffect which cleans up all elements that match the link[data-n-p] selector here https://github.com/vercel/next.js/blob/9de7705c9919aae57b7e79794bf0c9c9e67636e0/packages/next/src/client/index.tsx#L760. This removes the stylesheet element that mini-css-extract had just assumed was going to be in the document, which results in the component that uses that stylesheet to be unstyled when the client-side navigation completes.
Possible Fixes
mini-css-extract-plugin could be patched to not look for link tags with the data-n-p tag. This means that mini-css-extract-plugin would include another link tag, which would not be cleaned up via onHeadCommit when client-side navigation completes.
mini-css-extract-plugin could be patched to remove the data-n-p tag for any stylesheets that it looks up, which would stop the stylesheet from being removed by onHeadCommit when client-side navigation completes.
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
Link to the code that reproduces this issue
https://github.com/copperwall/mini-css-nextjs-repro
To Reproduce
Start the application in production mode
Navigate to
http://localhost:3000
, observe that the element with "Some text" has styling including background-color and a border.Click on the "To other page" link below to navigate to
http://localhost:3000/client_side_navigation
using client-side navigation vianext/link
.Observe that the element with "Some text" no longer has any styling associated with it. This component is rendered using the same component from the index page, except wrapped in a
next/dynamic
call, so in this case we'd expect to see the same background-color and border styles as the index page.If you reload
http://localhost:3000/client_side_navigation
you can observe the style it should haveCurrent vs. Expected behavior
Following the steps from the previous section, I'd expect the
/client_side_navigation
page to have the same style applied as the/
page, since they use the same component and share the same stylesheet.However, the
StyledBoxLazy
on the/client_side_navigation
page is unstyled when the user does a client-side navigation from/
to/client_side_navigation
.Provide environment information
Which area(s) are affected? (Select all that apply)
Dynamic imports (next/dynamic), Routing (next/router, next/navigation, next/link)
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Other (Deployed)
Additional context
We're running Next via Docker (outside Vercel) but we've been able to reproduce this both in prod and locally. Happy to answer any questions or investigate possible workarounds! From the repro repo I added what seems to be happening and some potential ways to fix it, but I'll include them here too
Explanation
The index page includes a component called
StyledBox
, which has a dependency on a CSS Module. The client-side-navigation page has a dependency onStyledBoxLazy
, which isStyledBox
wrapped withinnext/dyanmic
.The stylesheet is initially included when loading the server-side rendered version of the index page. Because the StyledBox component is not using
next/dyanmic
on the index page, the stylesheet link tag has adata-n-p
attribute.When the user navigates to client-side-navigation via clicking the
Link
component, the client-side-navigation page rendersStyledBoxLazy
, which attempts to load the CSS chunk associated withStyledBoxLazy
. In the webpack runtime chunk,mini-css-extract-plugin
looks up the file path via the chunk/module id and first checks to see if there is alink
tag with arel=stylesheet
and anhref
that matches the filepath for the stylesheet it needs to load. In this case it finds the stylesheet that was loaded via SSR on the initial index pageload, and does not add an additional link tag for that stylesheet (because one already exists in the document). This happens in this section of the plugin https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/index.js#L924.After the render phase for client-side-navigation finishes, the
onHeadCommit
method runs in auseLayoutEffect
which cleans up all elements that match thelink[data-n-p]
selector here https://github.com/vercel/next.js/blob/9de7705c9919aae57b7e79794bf0c9c9e67636e0/packages/next/src/client/index.tsx#L760. This removes the stylesheet element thatmini-css-extract
had just assumed was going to be in the document, which results in the component that uses that stylesheet to be unstyled when the client-side navigation completes.Possible Fixes
mini-css-extract-plugin
could be patched to not look forlink
tags with thedata-n-p
tag. This means thatmini-css-extract-plugin
would include anotherlink
tag, which would not be cleaned up viaonHeadCommit
when client-side navigation completes.mini-css-extract-plugin
could be patched to remove thedata-n-p
tag for any stylesheets that it looks up, which would stop the stylesheet from being removed byonHeadCommit
when client-side navigation completes.NEXT-2849