-
Notifications
You must be signed in to change notification settings - Fork 27k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
CSS module styling is removed too early on route changes #17464
Comments
I looked at this issue and this seems to be happening because of the behaviour implemented in Inside Exploring this further and will raise a PR for this in sometime |
Essentially what I ended up finding was: we should never set 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 |
Hi, experiencing the very same issue as reported. |
Experiencing the same issue. I also noticed the 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. |
Having looked into this, I can't see anywhere that media attribute is being changed, but I do see that the relevant 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, |
@fredcorr Yea that code is a bit out of context, as @Limekiller mentioned, the best place to put it is |
@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 |
For anyone needing a dirty quick fix for this one until a fix is released, simply importing the modules whose style is needed in |
@danieljamesross I was having the same issue with styled-components on page load or page refresh. Just needed to create a
This was mentioned in the docs somewhere but I overlooked it. Not sure if it will work for you but it did for me. |
This is working as a temporary fix. |
Make sure to clear the timeout when routeChange is called, otherwise when the user navigates back during the transition, the styles of the current page will be removed. Also when using query params like this: useEffect(() => {
const pathname = Router.router?.pathname
const query = Router.router?.query
Router.router?.push({ pathname, query })
}, []) a 404 or 500 will redirect to /_error instead of displaying an error message on the current page. This is my modification. It can probably be improved. Just wanted to point out these two issues because I already saw them in the wild. import Router from 'next/router'
import { useEffect } from 'react'
export const OPACITY_EXIT_DURATION = 1
export const useTransitionFix = () => {
useEffect(() => {
let timeout
const routeChange = () => {
clearTimeout(timeout)
const elements = document.querySelectorAll('style[media="x"]')
elements.forEach((elem) => elem.removeAttribute('media'))
timeout = setTimeout(() => {
elements.forEach((elem) => elem.remove())
}, OPACITY_EXIT_DURATION * 1000)
}
Router.events.on('routeChangeComplete', routeChange)
Router.events.on('routeChangeStart', routeChange)
return () => {
Router.events.off('routeChangeComplete', routeChange)
Router.events.off('routeChangeStart', routeChange)
clearTimeout(timeout)
}
}, [])
useEffect(() => {
const pathname = Router.router?.pathname
const query = Router.router?.query
if (pathname === '/_error') {
return
}
Router.router?.push({ pathname, query })
}, [])
return null
} |
had the same issue with work around above but my css module is in _app and the hack has flicker even without timeout |
…rably show the homepage tagline when navigating from home page to interior page. Note to self: follow vercel/next.js#17464 for resolution and revert this change if possible.
Using this fix https://github.com/moxystudio/next-with-moxy/blob/master/www/app/use-fouc-fix.js from @satazor has fixed any issues for us (calling |
I've got a workaround without using the fix by @satazor! By importing component dynamically (next/dynamic).....the problem of CSS-disabling goes away. // index.js
import dynamic from "next/dynamic";
const Component1 = dynamic(() => import("@path/component1"));
const Component2 = dynamic(() => import("@path/component2"));
export default function Home() {
return (
<>
<Component1/>
<Component2/>
</>
);
} However Page should contain only components & each component has its own CSS Module. This workaround works in my case Next.js 13.1.2 and framer-motion 8.5.2 |
As an alternative, I was looking for a way to completely avoid CSS splitting. It doesn't' make much sense to me, especially for tiny chunks. Changing the optimisation.splitchunks.minsize setting isn't enough, so apparently, there's no way to avoid CSS splitting (even for < 1kb file!). // your_script.js
const path = require('path');
const fs = require('fs');
const CSS_PATH = path.resolve(__dirname, './.next/static/css');
const CSS_FILENAMES = fs.readdirSync(CSS_PATH);
const GLOBAL_CSS_FILENAME = CSS_FILENAMES.find((file) => {
const FILE_PATH = path.join(CSS_PATH, file);
const data = fs.readFileSync(FILE_PATH, 'utf8');
return data.startsWith('html');
});
const GLOBAL_CSS_PATH = path.join(CSS_PATH, GLOBAL_CSS_FILENAME);
CSS_FILENAMES.filter((name) => name !== GLOBAL_CSS_FILENAME).forEach((file) => {
const FILE_PATH = path.join(CSS_PATH, file);
const data = fs.readFileSync(FILE_PATH, 'utf8');
fs.appendFileSync(GLOBAL_CSS_PATH, data);
fs.truncate(FILE_PATH, 0, () => console.log('done'));
}); {
"scripts": {
"build": "next build",
"postbuild": "node your_script.js"
}
} |
Oh, this thread is still alive. I have an update on the hook i posted here almost two years ago: #17464 (comment) The new version below solves a couple of things:
Just call this hook in your [[[ THIS IS BUGGY AND OUTDATED, SEE UPDATE ]]] import * as React from 'react';
export const useNextCssRemovalPrevention = () => {
React.useEffect(() => {
// Remove data-n-p attribute from all link nodes.
// This prevents Next.js from removing server rendered stylesheets.
document.querySelectorAll('head > link[data-n-p]').forEach(linkNode => {
linkNode.removeAttribute('data-n-p');
});
const mutationHandler = (mutations: MutationRecord[]) => {
mutations.forEach(({ target, addedNodes }: MutationRecord) => {
if (target.nodeName === 'HEAD') {
// Add data-n-href-perm attribute to all style nodes with attribute data-n-href,
// and remove data-n-href and media attributes from those nodes.
// This prevents Next.js from removing or disabling dynamic stylesheets.
addedNodes.forEach(node => {
const el = node as Element;
if (el.nodeName === 'STYLE' && el.hasAttribute('data-n-href')) {
const href = el.getAttribute('data-n-href');
if (href) {
el.setAttribute('data-n-href-perm', href);
el.removeAttribute('data-n-href');
el.removeAttribute('media');
}
}
});
// Remove all stylesheets that we don't need anymore
// (all except the two that were most recently added).
const styleNodes = document.querySelectorAll('head > style[data-n-href-perm]');
const requiredHrefs = new Set<string>();
for (let i = styleNodes.length - 1; i >= 0; i--) {
const el = styleNodes[i];
if (requiredHrefs.size < 2) {
const href = el.getAttribute('data-n-href-perm');
if (href) {
if (requiredHrefs.has(href)) {
el.parentNode!.removeChild(el);
} else {
requiredHrefs.add(href);
}
}
} else {
el.parentNode!.removeChild(el);
}
}
}
});
};
// Observe changes to the head element and its descendents.
const observer = new MutationObserver(mutationHandler);
observer.observe(document.head, { childList: true, subtree: true });
return () => {
// Disconnect the observer when the component unmounts.
observer.disconnect();
};
}, []);
}; |
For the Next team (cc @wyattjoh) To help mitigate this issue, maybe you could add a next.config.js flag that, if enabled, lets app developers opt out of the current behavior. A couple ideas how this could behave:
The flag could be called |
this worked for me. Just had to install babel-styled-components, create a babel config and paste in the setup to use the styled components plugin. So far so good! Thanks! |
@DangerousJack I think this works only if you are using babel and not SWC, which is not the case for most people as Next.js is using SWC by default these days |
Same problem here, i hope they will fix that soon (maybe in 1-2 years at this rate :3 ) but thx to @claus his solution works like a charm while waiting for a more official fix ! Thx dude |
with "next": "13.1.6" and "framer-motion": "^10.10.0", this page transition problem is still alive, and through above solutions, can solve the problem in my project. thank you! |
Just a heads up, that updated hook i posted above assumes that one page loads exactly one stylesheet. That's not always the case. In some configurations, pages load more than one stylesheet, breaking my hook. I'll have a fix for that soon. |
Ok, probably my last post on this issue :) We published a PageTransition suite of components, that includes the
Docs Demo |
@claus <3 |
Thank you @claus 🙏🏻 |
- Another dep for page transitions, previous one was causing page style breakage by removing css too early vercel/next.js#17464
Love U |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Bug report
Describe the bug
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 usestyled-jsx
, which is not my preferred method of styling my components for this project.To Reproduce
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.styled-jsx
Behavior: correct, no flash of unstyled content
Deployed site on Vercel
Repo
CSS modules
Behavior: buggy, there is a flash of unstyled content immediately after clicking the link
Deployed site on Vercel
Repo
Sass via CSS modules (additional)
Behavior: buggy, there is a flash of unstyled content immediately after clicking the link (same as CSS modules)
Deployed site on Vercel
Repo
Expected behavior
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.System information
NEXT-1351
The text was updated successfully, but these errors were encountered: