CSS module styling is removed immediately after clicking a next/link, instead of after the DOM is removed on production builds. This causes the components to have no styling at all during a page transition. This issue does not happen on dev mode.
I believe this is a bug with CSS modules specifically because components styled with styled-jsx don't have this problem.
Really would love to be able to use Sass via CSS modules here instead of re-writing the entire app I'm working on using styled-jsx. If Sass modules can't work in this scenario, I think I would be forced to use styled-jsx, which is not my preferred method of styling my components for this project.
I have created repos, and deployed these repos to demonstrate the problem using framer-motion for page transitions. If you were to pull these repos and run them locally using npm run dev, you will see that the flash of unstyled content does not happen on any one of them in dev mode. However, on their deployed sites, you can see the flash of unstyled content with CSS modules and Sass modules.
Behavior: correct, no flash of unstyled content
Deployed site on Vercel
Repo
Behavior: buggy, there is a flash of unstyled content immediately after clicking the link
Deployed site on Vercel
Repo
Behavior: buggy, there is a flash of unstyled content immediately after clicking the link (same as CSS modules)
Deployed site on Vercel
Repo
Styling for components that come from CSS modules should not be removed immediately on route changes, and instead, are removed when the markup is removed (the component unmounts?). The expected behavior is the behavior we can see on the styled-jsx deployment above.
I looked at this issue and this seems to be happening because of the behaviour implemented in onCommit method.
Inside onCommit, we are setting the media attribute to the style tags which are not desired too early.
Exploring this further and will raise a PR for this in sometime
Essentially what I ended up finding was: we should never set media attribute to x on the <style> tags since they might still be in use even though the page that was using it is being replaced. To do this, we simply need to get rid of the code that is executing in the else block of this loop in onCommit.
I don't know if this was done because of any specific reasons or not but this seems to solve the issue on my local after making these changes.
@lfades any thoughts? If it looks good to you then I'll go ahead and raise a PR for this
Also, to make this work and to have the <style> tags on SSRed page, we might need to remove the isInitialRender check in onStart and !isInitialRender check in onCommit
Hi, experiencing the very same issue as reported.
Experiencing the same issue. I also noticed the media attribute being set to x on the relevant style tags. Would love to avoid the switch to styled-jsx, as it doesn't play well with framer motion: https://github.com/framer/motion/issues/231
I've applied this hack in the mean time:
// Add that code to _app.tsx / _app.jsx
import Router from "next/router";
const routeChange = () => {
// Temporary fix to avoid flash of unstyled content
// during route transitions. Keep an eye on this
// issue and remove this code when resolved:
// https://github.com/vercel/next.js/issues/17464
const tempFix = () => {
const allStyleElems = document.querySelectorAll('style[media="x"]');
allStyleElems.forEach((elem) => {
elem.removeAttribute("media");
});
};
tempFix();
};
Router.events.on("routeChangeComplete", routeChange );
Router.events.on("routeChangeStart", routeChange );
I am not sure what other unwished side effects this temporary fix could have, but it seems to work just well enough for my application.
I'm having the same issue, but @scriptify's fix does not work for me, any ETA for a fix for this? @MihirGH
@tommhuth It is actually a quick fix but I am not sure if that's how it is supposed to be solved or not.
@MihirGH That code is just a temporary workaround, it also possible that Next.js is setting the media attribute slightly after those events fire, maybe that's the reason why it's not working for @tommhuth. @tommhuth are you sure that in your case the problem is also related to the media attribute?
Having looked into this, I can't see anywhere that media attribute is being changed, but I do see that the relevant link has its rel set from stylesheet to preload. If I manually revert it to stylesheet the appropriate styles are back, but when I do this in a routeChangeComplete/routeChangeComplete event callback there is a flash of unstyled content so those events are probably not accurate enough to serve as a quick fix. Also, I don't think I have any way of knowing exactly what style link is needed, so I end up setting all links with css files back to stylesheet which might be unnecessary. @scriptify @MihirGH
Off the top of my head, the only way I could get around this is by moving all style modules into non-module SCSS, but that is quite a significant workaround.
Hmm that's strange, seems like a severe issue to me, and moving everything into non modular CSS seems like a very tedious task, just to make it modular again when it's fixed (besides the whole disadvantages global CSS has). Are we the only ones using CSS Modules + non-instant route changes? 🤷♂️ Doesn't seem too exotic to me
@scriptify I was running into this issue as well; your hack worked for me for the time being, so thanks for that.
@scriptify I bumped into the same issue. could you help me understand where I would add the fix you wrote? Thanks for your help as well
@fredcorr I put the code in _app.js along with, of course, import Router from 'next/router'
@fredcorr Yea that code is a bit out of context, as @Limekiller mentioned, the best place to put it is _app.tsx / _app.jsx. I updated my comment accordingly to clear that up.
@Limekiller @scriptify thanks guys that fixed the issue partially, on the first-page transition the issue still occurs. Are you guys using the getStaticProps or GgetServerSideProps? Could that maybe affect it? @MihirGH Any updated on how long will it take to fix this?
@fredcorr I'm using getStaticProps for all my pages for this particular project, but I don't think that should affect the route transition behaviour, just speculating ofc. Would be nice to get some hints from the Next.js team 🤗
For anyone needing a dirty quick fix for this one until a fix is released, simply importing the modules whose style is needed in _app.js solved it for me without requiring any other refactoring.
Most helpful comment
Hi, experiencing the very same issue as reported.