Go Back

Outputting multiple CSS files with Vite

Posted: 
Last modified: 

I'm building a component library for our team and am on a wild ride of understanding the module systems of JavaScript. The latest part of that journey involves attempting to output multiple css files with the bundler Vite.

Vite only outputs a single CSS file

No matter what I did, it couldn't seem to get Vite to output multiple CSS files. Using library mode and multiple entry points still only resulted in a single CSS file.

I also tried using the rollupOptions property in the Vite config to manually specify inputs and output, but I never came to a satisfactory solution. I can't exactly remember why which is reminding me to take notes on things like this as I go (I didn't take notes because I didn't realize this was going to be such a long arduous journey!).

Vite JavaScript API

Vite actually gives us a couple functions that allow us to trigger functionality of Vite manually with our own scripts. The build function that Vite exports can trigger a Vite build from a JavaScript file.

The loop is the thing

Since we know that Vite outputs a single CSS file per build, I figured we would just need to run multiple builds. A loop that would run a separate build for each entry point seemed the way to go.

I created an object that would be a library type object with keys as the final output file and the value being the source entry point.

const entries = {
components: "src/components",
hooks: "src/hooks",
utils: "src/utils",
"theme/v2": "src/theme",
}

Now we can loop over this and provide the appropriate values within the loop.

for (const [key, value] of Object.entries(entries)) {
await build({
plugins: [dts()],
build: {
emptyOutDir: false,
lib: {
entry: { [key]: new URL(value, import.meta.url).pathname },
formats: ['es'],
fileName: (format, entryName) => <code>${entryName}.js</code>,
},
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react/jsx-runtime': 'react/jsx-runtime',
},
assetFileNames: () => `${key}[extname]`,
},
},
},
});
}

Some things to note:

  • emptyOutDir - If we don't specify this then it will overwrite the previous build each time.
  • entry - This is where we use the value from the loop to populate the entry. We're only specifying a single entry point per build so that we'll get our separate CSS file
  • assetFileNames - This is was defines the name of the css file. We're matching it to the entry point name with the appropriate extension so that we know which CSS file is for which build.

Next steps

We do some additional moving of files in the loop after the build but that relates more to how we're structuring our module. Details on that will follow in a blog about subpath exports. You can read my initial blog post on the subject called The saga of learning the exports property in package.json.

Reference

This was a twitter thread where Wes Bos went through a similar process but solved it a different way.

"Anyone using Vite library mode for multiple css files?" -
https://twitter.com/wesbos/status/1621247972515233796