vite: Vite injects css assets in wrong order with dynamic import and css modules.
Describe the bug
Vite injects css assets in wrong order with dynamic import and css modules.
Reproduction
For example:
- You have component Button with default styles (text color: red)
/* button.tsx */
import React from "react"
import classnames from 'classnames'
import styles from './button.module.css'
type ButtonProps = {
children: string
className?: string
onClick?: () => void
}
function Button({ children, className, onClick }: ButtonProps) {
return <button className={classnames(styles.button, className)} onClick={onClick}>{children}</button>
}
export default Button
/* button.module.css */
.button {
color: red
}
- You have Page component. This page use Button component and overrides it styles by passing custom class name as a prop (text color: green)
/* home.tsx /*
import React from "react"
import Button from "../../components/button/button"
import styles from './home.module.css'
function HomePage() {
return <div>
<h1>Home page</h1>
<Button className={styles.greenTextButton}>should be green</Button>
</div>
}
export default HomePage
/* home.module.css */
.greenTextButton {
color: green;
}
- You import Page component with dynamic import.
import React, { lazy, Suspense, useState } from 'react'
const HomePage = lazy(() => import('./pages/home/home'))
const AboutPage = lazy(() => import('./pages/about/about'))
function App() {
const [page, setPage] = useState('home')
return (
<div>
<a href="#" onClick={() => setPage('home')} style={{ marginRight: '5px' }}>home page</a>
<a href="#" onClick={() => setPage('about')}>about page</a>
<Suspense fallback={<div>loading</div>}>
{page === 'home' && (
<HomePage />
)}
{page === 'about' && (
<AboutPage />
)}
</Suspense>
</div>
)
}
export default App
- You exepect page styles will override button styles, but they are not (as vite injects styles in wrong order)
P.S. You can reproduce this but with cssCodeSplit: true or false.
System Info
Output of npx envinfo --system --npmPackages vite,@vitejs/plugin-vue --binaries --browsers
:
System:
OS: macOS 11.4
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 94.85 MB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 14.16.1 - ~/.volta/tools/image/node/14.16.1/bin/node
npm: 6.14.12 - ~/.volta/tools/image/node/14.16.1/bin/npm
Browsers:
Chrome: 91.0.4472.114
Firefox: 89.0
Safari: 14.1.1
npmPackages:
vite: ^2.3.8 => 2.3.8
Used package manager:
Logs
vite:config bundled config file loaded in 42ms +0ms
vite:config using resolved config: {
vite:config plugins: [
vite:config 'alias',
vite:config 'react-refresh',
vite:config 'vite:dynamic-import-polyfill',
vite:config 'vite:resolve',
vite:config 'vite:html',
vite:config 'vite:css',
vite:config 'vite:esbuild',
vite:config 'vite:json',
vite:config 'vite:wasm',
vite:config 'vite:worker',
vite:config 'vite:asset',
vite:config 'vite:define',
vite:config 'vite:css-post',
vite:config 'vite:build-html',
vite:config 'commonjs',
vite:config 'vite:data-uri',
vite:config 'rollup-plugin-dynamic-import-variables',
vite:config 'vite:import-analysis',
vite:config 'vite:esbuild-transpile',
vite:config 'vite:terser',
vite:config 'vite:reporter'
vite:config ],
vite:config build: {
vite:config target: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
vite:config polyfillDynamicImport: false,
vite:config outDir: 'dist',
vite:config assetsDir: 'assets',
vite:config assetsInlineLimit: 4096,
vite:config cssCodeSplit: true,
vite:config sourcemap: false,
vite:config rollupOptions: {},
vite:config commonjsOptions: { include: [Array], extensions: [Array] },
vite:config minify: 'terser',
vite:config terserOptions: {},
vite:config cleanCssOptions: {},
vite:config write: true,
vite:config emptyOutDir: null,
vite:config manifest: false,
vite:config lib: false,
vite:config ssr: false,
vite:config ssrManifest: false,
vite:config brotliSize: true,
vite:config chunkSizeWarningLimit: 500,
vite:config watch: null
vite:config },
vite:config configFile: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/vite.config.ts',
vite:config configFileDependencies: [ 'vite.config.ts' ],
vite:config inlineConfig: {
vite:config root: undefined,
vite:config base: undefined,
vite:config mode: undefined,
vite:config configFile: undefined,
vite:config logLevel: undefined,
vite:config clearScreen: undefined,
vite:config build: {}
vite:config },
vite:config root: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order',
vite:config base: '/',
vite:config resolve: { dedupe: undefined, alias: [ [Object] ] },
vite:config publicDir: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/public',
vite:config cacheDir: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/node_modules/.vite',
vite:config command: 'build',
vite:config mode: 'production',
vite:config isProduction: true,
vite:config server: {
vite:config fsServe: {
vite:config root: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order',
vite:config strict: false
vite:config }
vite:config },
vite:config env: { BASE_URL: '/', MODE: 'production', DEV: false, PROD: true },
vite:config assetsInclude: [Function: assetsInclude],
vite:config logger: {
vite:config hasWarned: false,
vite:config info: [Function: info],
vite:config warn: [Function: warn],
vite:config warnOnce: [Function: warnOnce],
vite:config error: [Function: error],
vite:config clearScreen: [Function: clearScreen]
vite:config },
vite:config createResolver: [Function: createResolver],
vite:config optimizeDeps: { esbuildOptions: { keepNames: undefined } }
vite:config } +6ms
vite v2.3.8 building for production...
✓ 33 modules transformed.
dist/assets/favicon.17e50649.svg 1.49kb
dist/index.html 0.45kb
dist/assets/home.5d3a6e0a.js 0.26kb / brotli: 0.15kb
dist/assets/button.14aa6fb9.js 0.80kb / brotli: 0.41kb
dist/assets/home.deac2baa.css 0.04kb / brotli: 0.04kb
dist/assets/about.563410d2.js 0.60kb / brotli: 0.28kb
dist/assets/button.a44dba50.css 0.03kb / brotli: 0.03kb
dist/assets/index.3938cd91.js 1.55kb / brotli: 0.60kb
dist/assets/about.d57e38e3.css 0.06kb / brotli: 0.05kb
dist/assets/vendor.cc984a25.js 127.61kb / brotli: 36.05kb
Before submitting the issue, please make sure you do the following
- [ +] Read the Contributing Guidelines.
- [ +] Read the docs.
- [ +] Check that there isn’t already an issue that reports the same bug to avoid creating a duplicate.
- [ +] Provide a description in this issue that describes the bug.
- [+] Make sure this is a Vite issue and not a framework-specific issue. For example, if it’s a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/vue-next instead.
- [ +] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 85
- Comments: 61 (19 by maintainers)
Links to this issue
Commits related to this issue
- test: validate dynamic import ordering Looks like my hack in https://github.com/tivac/modular-css/blob/4af74248fb2bda844dd8c0a2ef3831e2edd94f26/packages/vite/vite.js#L196-L203 totally does fix the is... — committed to tivac/modular-css by tivac a year ago
- More playwright (#928) * test: test built vite sites as well * test: validate dynamic import ordering Looks like my hack in https://github.com/tivac/modular-css/blob/4af74248fb2bda844dd8c0a2ef3831e... — committed to tivac/modular-css by tivac a year ago
- Popup improvements and acknowledgement of existing vite bug: https://github.com/vitejs/vite/issues/3924 — committed to budlinville/tuple by budlinville 8 months ago
This seems to still be an issue. Really killing me. Any updates?
This is a pretty big issue. The production build styling order also differs in order compared with the dev build making working with Vite a nightmare for me. Is there no workaround I can use in the meanwhile, it seems we are a long way from having this fixed?
Any progress on this issue please? I’m having the same problem when packaging my application using the Federation module.
is there any update on this?
In my case css injection seem to be sorted by name.
replace
app
withzpp
inmanualChunks
@poyoho Can we please get an update on the progress of this and the open PR https://github.com/vitejs/vite/pull/9278?
This is a really big issue which is now open for over one and a half year and more. Is there any progress? anything on the horizon? We are using BEM. But with this problem parent component is not able to overwrite child css with a class which forces us to do parent > child selectors to get higher specifity which is against what we want to do with BEM. So we are a bit in trouble.
Any workaround on this, or idea when this will get solved?
While this is frustrating, I was able to work around it using CSS layers.
If you’re worried about browser support, there is a PostCSS plugin to help with that.
The only way to preserve CSS ordering when bundling is to bundle all CSS into 1 CSS file per-page. Once you chunk common dependencies you are creating scenarios where ordering will be different on some pages. In Astro we are reconsidering whether to use the
:where
selector due to this. We might also come up with some other solutions. Just chiming in that the underlying problem is probably unsolvable.has this issue perfectly resolved? Or still working on fixing?
Speaking of cascade layers, if you’re using css modules (which is likely if you’re hitting this issue), you could try out https://github.com/DefinedNet/postcss-assign-layer to assign all of your components to a separate layer from your global / utility layers. This doesn’t work with the cascade layers polyfill, but I think browser support is good enough you shouldn’t need the polyfill anymore.
Another option for avoiding this issue is to not code-split your app. There are tradeoffs there, but it’s something to consider.
Applying styles in the wrong order is a deal breaker for using vite. I don’t understand - why is this issue still open and more importantly, how are people actually developing with vite when you cannot trust the css order?
For those who (like me) had to ship and need a temporary workaround: Depending on your project, you may be able to use
build.cssCodeSplit
to extract a single css file which has the classes in the correct order.the same issue for vue vite
I’ve also encountered this issue as we upgrade a legacy project. The load order of CSS is important. Why is there no option to specify the order within the manifest.json? CSS order is, unfortunately, a real concern. Seeing how many people are interacting with this issue, I wonder why there hasn’t been a solution proposed.
This reference could help with this issue?
Vite css.modules
Same
hay!
It’s almost finished now, but it’s stuck at one point. Now the loading order should be as expected, but there is still a problem with the execution order
Why pre-load
blue.module.css
but execute first__vite__updateStyle
is post-loadred.module.css
🥲However change the import order I do, it always load order by
red -> green -> blue
in dev mod.sadly this didnt work out for me @laurentvd 😔 i might need to refactor my components 🤔
Great work @IanVS! Really hope this helps resolving the issue. I would help, but have no idea on where to start unfortunately.
How did you solve it
Because it isn’t fixed yet. Have you considered that this may be a difficult problem?
There are solutions to manage cascade order that do not depend on CSS order. Have you considered reading the thread that you’re commenting on?
I’m having related a related issue but perhaps also different? Every time I run vite build it slices all of the css code above the media queries in one of my components off. I checked the compiled css and half of that module is no where to be found. Any update on this? I tried disabling code splitting but it didn’t seem to work, or perhaps my config file was wrong…
I see the wisdom in Remix of having “per-page” css loaders in this case - JS imports and CSS cascade generally follow an inverse relationship, right? So the most correct CSS cascade will need about one CSS chunk per page…
I’m having the same problem when packaging multiple page apps.
I have the same issue. Vite --host works fine, but code after Vite build mixes order css files. My workaround is !important property but it is crap
As far as I know, it is not fixed and I don’t think anyone is actively working on it. @poyoho made some heroic efforts on it, but was never able to handle all the edge cases. I wonder if code splitting is just fundamentally incompatible with css modules.
@poyoho Any progress?
As far as I know it’s not ready, @mgiraldo. This is a sticky problem to solve. If you have any ideas or can help out, please do!