This repository servers as a minimal reproduction of an error that occurs in Next that occurs around loading stylesheets with next/dyanmic.
The bug occurs under the following conditions on a production build (using the pages router)
- User loads a page that contains a stylesheet which is loaded with a
data-n-ptag because it is included for a component that was server side rendered. The style shows up correctly. - User clicks on a
Linkcomponent fromnext/linkwhich will use client-side navigation to load a second page. - The second page includes a component which is rendered via
next/dynamicwhich also has a dependency on the stylesheet that is loaded from the first page. - The lazy component on the second page is missing the styles from the stylesheet
$ npm run build && npm run start- Go to http://localhost:3000. Observe that the main component has
borderandbackground-colorstyles.
- Click the
To other pagelink to client-side navigate to http://localhost:3000/client-side-navigation. Observe that the styles on the page are no longer applied. The component on this page is a lazy version of the one on the previous page, so it should have the styles associated with its CSS Module applied to it.
- Navigate to http://localhost:3000/client-side-navigation directly, observe that the styles are showing up now.
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.
mini-css-extract-plugincould be patched to not look forlinktags with thedata-n-ptag. This means thatmini-css-extract-pluginwould include anotherlinktag, which would not be cleaned up viaonHeadCommitwhen client-side navigation completes.mini-css-extract-plugincould be patched to remove thedata-n-ptag for any stylesheets that it looks up, which would stop the stylesheet from being removed byonHeadCommitwhen client-side navigation completes.