-
Notifications
You must be signed in to change notification settings - Fork 28k
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
Inconsistent CSS resolution order with App Router #64921
Comments
It seems |
@GabenGar Thanks for sharing a |
We are seeing the same issue on The specific example we are seeing is that CSS styles imported into layout.js are overriding CSS styles set in a component level stylesheet even though they have the same CSS specificity and the component CSS should override the layout's CSS. This bug appears to be due to the order that the CSS is included in the final static .css files that are included in the production build. |
@benjitastic What kind of CSS styling are you using in this case (e.g., css modules)? |
No, not modules. Just like this inside the component:
Some more details: In this case layout.js had this at the top: And customTheme.scss had this inside it: That bootstrap file has a css style declared for .btn like this: Then in the component we have an element The expectation is that .filter-pill padding can override .btn padding. But .btn was overriding .filter-pill styles. This was because of the 5 Hard to post a repro since I think you need a project that has enough CSS to result in multiple static CSS files being generated. We reverted back to 14.1.4 and the CSS went back to the correct order. |
I am seeing this issue as well, particularly for global styles as well as styles using css modules. As mentioned in this issue, it only happens in production builds. |
Setting |
Interesting -- I can't find any docs anywhere on the Does anybody know exactly what "strict" css chunking does? |
It does not seem to resolve the issue for us :( |
This comment has been minimized.
This comment has been minimized.
@Netail Thanks for sharing.
I can confirm there are several broken cases with the ordering of CSS, after looking at several
Getting some answers internally to further clarify this—will respond back soon! |
That would be great. We use a design system package and a navigation package which uses the design system package (with some overrides) and the app using the design system, but the overwrites are currently not working on productions. Thus making NextJS kind of unusable currently for us. So the sooner the better 😅 Do you by any chance have a ETA when development on this will happen? |
Can confirm this issue also comes up in a project using Next.js 14.2., Mantine v7.10 components, and css modules. Works fine in development mode, loads incorrectly in production. |
I had a similar issue, where global styles were bundled after component styles. Running dev I never had an issue, only on production. I'm using Next 14.2.2 with App router and SSG. My workaround is only for getting global scss that's imported in layout.js to load ahead of client component scss modules. But perhaps this will be helpful for someone else / debugging the overall issue. In my root layout.js I was importing /global.scss There is an Indeed there was css rules added above the global.scss. After analyzing it seems that the css above my My workaround fix is to make a new client component that imports the styles "use client";
import "@/scss/global.scss";
const GlobalStyles = () => {
return <></>;
};
export default GlobalStyles; and then import that component in my root layout.js import GlobalStyles from "./GlobalStyles";
export default function RootLayout({ children }) {
return (
<html lang="en">
<GlobalStyles />
<body>
{children}
</body>
</html>
);
} This resolved the issues I was getting in dev tools, and also some issues with specificity (component styles were no longer overriding global styles before the workaround). |
@Netail No ETA to share yet, but this issue is definitely high on our plate! |
I noticed that if I replace I believe this regression was introduced here as part of the 14.2 release. Edit: It appears that removing the sorting also results in the correct order |
@piratetaco Is it possible to @michaelkostal Can you try testing a later Next.js version? I believe this |
@samcx I face this issue on 14.2.3 as well. Can we backport the additional bug fix to v14 as well for those who cannot switch to a canary version or upgrade a major version at this time? |
@paulyi The latest changes should now be in 14.2.5 (includes both fixes mentioned above). |
@samcx just tried out the 14.2.5 release -- while this release is an improvement, I'm still seeing some incorrect loading of css in the production environment. |
Can you describe exactly how it's loading incorrectly, and is it possible to provide a minimal, public |
It'll take me a bit to build a public repro for you, but I'll see if I have some time this weekend. The project I'm working on uses Mantine UI for our component library, which has a base styles css file that must be loaded first. Those are imported at the top of our |
@samcx upgrading to 14.2.5 did resolve my issue. It now appears my global styles are loaded first in order as expected. Thanks! |
@mrabuse @michaelkostal That's great to hear! |
sadly I'm unable to get a public repro going. The issue only seems to appear after the components are mixing and matching through the entire component library we're maintaining - but the issue we are seeing is still happening as of 14.2.5. For now we are going to import higher stylesheets in the component.js in our library as a cumbersome workaround.
|
The issue still persist as of 14.2.5 version, I'm using Sass with CSS Modules and I get inconsistent css import order between running the dev server locally and the production build, my app is quite big and I cannot get you a public repo up. Also this doesn't happen on small projects where only 1 chunk of css is build, in my case I have 5/6 chunks of css being build and I can't really reproduce something of that magnitude. ![]() ![]() In this case its a composed component from an atom where I want to overwrite the gap, locally all works as expected but when building the project the css imported chunks order is being mixed. My only solution at the moment is the one suggested by @piratetaco but I have a huge project, please fix this! |
+1 on this only surfacing once your app generates multiple chunk files. Your explanation of the issue matches exactly the symptoms we reported back in May. I'm still sitting at v14.1.4 and we are waiting to upgrade until this has been resolved, we are not yet considering implementing any work-arounds described in this issue thread as they are cumbersome to implement and maintain. |
I saw this issue on our project when trying to migrate to App Router and created this minimal repro here: https://github.com/saltycrane/repros/tree/main/next-css-modules-order When running But when running I don't understand the cause of the issue. It seemed to be related to which files import which files. If I move components to different files, the results change. I am using Node.js v22.9.0 on macOS Sonoma and the following npm dependencies:
|
@samcx any updates on this? Have a similar issue as @saltycrane described above. I tried a different next.js version and upgraded to the latest but the problem persists. This is happening only when running |
@naimlatifi5 One fix is still being worked on → #70087 |
@samcx Will this fix land in a 14.x patch or only 15? We're affected by this but still have a bit of work to do to get to Next 15. |
@chaance We should be able to backport that PR once it lands! |
Probably you already know this, but I found the issue for me was introduced in |
Until it's not fixed in nextjs, code below resolves the issue for me:
|
Some people have mentioned that global styles are loaded after individual modules, and this issue might be related to the problem described in the issue below—the CSS order gets messed up when client components are loaded. Additionally, I've commented on a potential cause of this problem. Could someone with expertise in webpack processes please take a look? |
Solved it by disable the minify classnames plugin and wrote my own, works as magic |
Thx, its work. Maybe need it only for dev? |
#64921 (comment) doesn't help. Any updates here? |
Disable CSS chunking due to persistent nextjs bug with ordering of css and this seems to help partially. Nextjs in production build only! puts global styles last so it messes up css specificity. We get css modules styles twice but atleast the second css file overwrites them in correct order. I think it's actually two issues, first is with sideEffects and external packages, because beeai-ui doesn't have `sideEffects: false` in package.json and I don't wanna add it because it's vite app, not a library and we have single `index.ts` in it that exports everything, nextjs bundler doesn't tree shake and sees all styles as required in a root layout. Having separate exports in package.json helps but doesn't mitigrate the issue completely see second issue bellow. The second issue is IMHO when the same component is imported from the page and layout and from RSC and from client component simultaneously, this breaks nextjs and as a result puts global styles after css modules styles. In our codebase it's a case of a Container component. I wasn't able to refactor this cleanly hence this workaround vercel/next.js#68207 vercel/next.js#64921
@mAdutskevich Unfortunately the PR above is still open (the one with Curious if |
@samcx Checked cssChunking true/false/'strict', unfortunately doesn't help. |
* feat(web): active nav link * fix(web): inperfect workaround for broken css ordering Disable CSS chunking due to persistent nextjs bug with ordering of css and this seems to help partially. Nextjs in production build only! puts global styles last so it messes up css specificity. We get css modules styles twice but atleast the second css file overwrites them in correct order. I think it's actually two issues, first is with sideEffects and external packages, because beeai-ui doesn't have `sideEffects: false` in package.json and I don't wanna add it because it's vite app, not a library and we have single `index.ts` in it that exports everything, nextjs bundler doesn't tree shake and sees all styles as required in a root layout. Having separate exports in package.json helps but doesn't mitigrate the issue completely see second issue bellow. The second issue is IMHO when the same component is imported from the page and layout and from RSC and from client component simultaneously, this breaks nextjs and as a result puts global styles after css modules styles. In our codebase it's a case of a Container component. I wasn't able to refactor this cleanly hence this workaround vercel/next.js#68207 vercel/next.js#64921
Created a temporary fix that worked for me. Let me know if you come across any issues along with the error message. Edited: Setting layer on CSS that comes from node_modules instead of scss modules now (kept the scss modules as non-layered)
For point 3. const postcss = require('postcss');
module.exports = () => ({
postcssPlugin: 'postcss-layer-wrapper',
Once(root, { result }) {
// Get the file path from the result object
const filePath = result.opts.from;
// Only proceed if it's from node_modules
if (!filePath || !filePath.includes('node_modules')) {
return;
}
const layerRule = postcss.atRule({ name: 'layer', params: 'external' });
const nodesToMove = [];
// Collect nodes that can be safely wrapped
root.each((node) => {
// Skip @import, @charset, and other non-layerable nodes
if (node.type === 'atrule' && ['import', 'charset'].includes(node.name))
return;
nodesToMove.push(node);
});
// Move collected nodes
nodesToMove.forEach((node) => {
node.remove();
layerRule.append(node);
});
// Add layer only if it contains nodes
if (layerRule.nodes && layerRule.nodes.length > 0) {
root.prepend(layerRule);
}
},
});
module.exports.postcss = true; nextjs/postcss.config.js module.exports = {
plugins: [
// include default Next.js plugins
'postcss-flexbugs-fixes',
['postcss-preset-env', {
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
features: {
'custom-properties': false,
},
}],
// custom plugin
'./postcss-plugins/postcss-layer-wrapper.js',
// cssnano should come last in production
process.env.NODE_ENV === 'production' ? 'cssnano' : undefined,
].filter(Boolean),
};
<style>@layer global, external;</style> |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@samcx I'm experiencing the same issue with Next.js version 15.2.2. The CSS resolution order is inconsistent between development and production, which is causing styling issues in my application. I've tried upgrading to the latest version, but the problem persists. Could the maintainers provide any updates on when this might be fixed? Are there any known workarounds in the meantime? Thanks! |
I can confirm this. This causes problems for every selector with same weight level. |
@mAdutskevich @GlebKodrik Creating a specific caching group for styles for production build works for us, note that we didn't go yet to PROD with this as we are trying to do a regression testing to see if it impacts the app in any other way, we have noticed that this way it creates a bit more css chunks but smaller in size. I think is more a Webpack issue than a Next.js issue, at least for us.
/** @type {import('next').NextConfig} */
const nextConfig = (phase) => ({
// Your existing config here
webpack: (config, { isServer }) => {
// Your existing webpack config here
if (!isServer) {
// Create a styles cache group that inherits from default but applies only to CSS
if (phase === PHASE_PRODUCTION_BUILD) {
config.optimization.splitChunks.cacheGroups.styles = {
...config.optimization.splitChunks.cacheGroups.default,
test: /\.(css|scss|sass)$/,
enforce: true,
};
}
}
return config;
},
})
module.exports = nextConfig |
We got around this using @layer and adding this snippet to the head.
|
Link to the code that reproduces this issue
https://github.com/GabenGar/repros/blob/main/nextjs/css-out-of-order/README.md
To Reproduce
Reproduction steps are in the README.md
Current vs. Expected behavior
Current:
Different CSS resolution order between development and production. Before I had weird client vs. render CSS issues, but it looks like they are fixed in 14.2, although they weren't super reproducible before either.
Expected:
Work basically like
pages
router.Provide environment information
Which area(s) are affected? (Select all that apply)
Not sure
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Vercel (Deployed)
Additional context
No response
The text was updated successfully, but these errors were encountered: