Vite configuration for better bundle monitoring

RelativeCI bundle analyzer1 works with any Vite configuration, though some configuration options can make it easier to identify changes between builds and notice regressions quicker.

Use a common pattern with content hashes for filenames

Adding the content hash suffix to asset names helps browsers cache the responses and only request from the server when the content of the asset changes. This method improves the performance for returning users because they only need to download the changed assets.

When comparing asset names between different builds, bundle-stats1 removes the hash and compares the assets by the normalized path. The supported patterns are:

# hex hash
**/*.HASH.EXT
**/*-HASH.EXT
**/*_HASH.EXT
**/*~HASH.EXT
# base64url hash
**/*.HASH.EXT
**/*~HASH.EXT

Vite 4/Rollup 3 default filename pattern (**/*-HEX_HASH.EXT) is supported by default, though for Vite 5/Rollup 4 we need to configure Rollup to use a supported pattern:

vite.config.js
import { defineConfig } from 'vite';
import { webpackStats } from 'rollup-plugin-webpack-stats';
export default defineConfig((env) => ({
build: {
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[hash][extname]',
chunkFileNames: 'assets/[name].[hash].js',
entryFileNames: 'assets/[name].[hash].js',
},
},
},
plugins: [
webpackStats(),
],
}));

npm run build
vite build
vite v5.0.0 building for production...
✓ 1862 modules transformed.
dist/index.html 0.31 kB │ gzip: 0.22 kB
dist/assets/utils.8L8ZNlhE.css 0.03 kB │ gzip: 0.05 kB
dist/assets/index._deYuKRK.css 546.34 kB │ gzip: 66.79 kB
dist/assets/repo-list.KTrC4ioP.js 0.55 kB │ gzip: 0.38 kB
dist/assets/CloseOutlined.Ac2Sglwr.js 0.66 kB │ gzip: 0.47 kB
dist/assets/col.KPyWUTbA.js 4.48 kB │ gzip: 1.84 kB
dist/assets/utils.pw5xtUsQ.js 40.12 kB │ gzip: 13.12 kB
dist/assets/about._IcwKZoH.js 79.56 kB │ gzip: 26.66 kB
dist/assets/repo-details.O1eeDOyK.js 117.49 kB │ gzip: 36.84 kB
dist/assets/index.rdoY6xeT.js 414.28 kB │ gzip: 136.95 kB
✓ built in 2.94s

Set unique names for asynchronous chunks

Asynchronous chunks defer code loading/execution, decrease the initial JS/CSS size, and improve the loading performance.

By default, Rollup extracts the chunk name from the entry module basename and uses it to generate the asset filename using the output.chunkFileNames configuration.

ModuleChunk nameFilename
./about.tsx
await import('./about')
aboutassets/about.CONTENT_HASH_1.js
./home.tsx
await import('./home')
homeassets/home.CONTENT_HASH_1.js
./about/index.tsx
await import('./index')
indexassets/index.CONTENT_HASH_2.js
./home/index.tsx
await import('./index')
indexassets/index.CONTENT_HASH_3.js

For example, when importing from directory index modules, Rollup extracts multiple chunks with the same name index:

const RepoDetailsAsync = React.lazy(
async () => {
// import components/repo-details/index.js
const chunk = await import('./components/repo-details');
return { default: chunk.RepoDetails };
},
);
const RepoListAsync = React.lazy(
async () => {
// import components/repo-list/index.js
const chunk = await import('./components/repo-list');
return { default: chunk.RepoList };
},
);
const AboutAsync = React.lazy(
async () => {
// import components/about/index.js
const chunk = await import('./components/about');
return { default: chunk.About };
},
);

npm run build
$ vite build
vite v5.0.0 building for production...
dist/index.html 0.31 kB │ gzip: 0.22 kB
dist/assets/index.8L8ZNlhE.css 0.03 kB │ gzip: 0.05 kB
dist/assets/index._deYuKRK.css 546.34 kB │ gzip: 66.79 kB
dist/assets/index.6DnQ4OBO.js 0.55 kB │ gzip: 0.37 kB
dist/assets/CloseOutlined.OIXdgtwq.js 0.66 kB │ gzip: 0.47 kB
dist/assets/col.3PcwYSue.js 4.48 kB │ gzip: 1.85 kB
dist/assets/index.PzG5MeZ4.js 40.12 kB │ gzip: 13.12 kB
dist/assets/index.gn3unjAa.js 79.56 kB │ gzip: 26.66 kB
dist/assets/index.gEtOlbAq.js 117.49 kB │ gzip: 36.84 kB
dist/assets/index.NooaB8q3.js 414.26 kB │ gzip: 136.94 kB
✓ built in 2.90s

When generating multiple assets with the same path + basename (e.g., PATH/index-HASH_1.js, PATH/index-HASH_2.js), bundle-stats1 is not able to match changed assets between builds and cannot show the chunk module comparison.

To set unique chunk names, you can use the chunkFileNames option to set the name dynamically based on the available information:

Example: set chunk name and file name based on the parent directory of the entry module

vite.config.js
import { defineConfig } from 'vite';
import { webpackStats } from 'rollup-plugin-webpack-stats';
export default defineConfig((env) => ({
build: {
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[hash][extname]',
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: (chunkInfo) => {
if (chunkInfo.name === 'index') {
const entryModule = chunkInfo.moduleIds[chunkInfo.moduleIds.length - 1];
const segments = path.dirname(entryModule).split('/');
const segment = segments[segments.length - 1];
chunkInfo.name = segment;
return `assets/component-${segment}.[hash].js`;
}
return 'assets/[name].[hash].js';
},
},
},
},
plugins: [
webpackStats(),
],
}));

npm run build
$ vite build
vite v5.0.0 building for production...
dist/index.html 0.31 kB │ gzip: 0.22 kB
dist/assets/repo.8L8ZNlhE.css 0.03 kB │ gzip: 0.05 kB
dist/assets/index._deYuKRK.css 546.34 kB │ gzip: 66.79 kB
dist/assets/component-repo-list.ugpgMnKd.js 0.56 kB │ gzip: 0.38 kB
dist/assets/CloseOutlined.0fpL38AF.js 0.66 kB │ gzip: 0.47 kB
dist/assets/col.1yOZU9sX.js 4.48 kB │ gzip: 1.84 kB
dist/assets/component-repo.SIhLG6w7.js 40.12 kB │ gzip: 13.12 kB
dist/assets/component-about._xXEM7JY.js 79.56 kB │ gzip: 26.66 kB
dist/assets/component-repo-details.BUE-6VoT.js 117.50 kB │ gzip: 36.85 kB
dist/assets/index.UC00wnqI.js 414.35 kB │ gzip: 136.96 kB
✓ built in 3.00s


[1]: bundle-stats is an open-source standalone tool that analyzes webpack/rspack/vite/rollup bundle stats and generates an in-depth report with insights and metrics for assets, modules, and packages. Read more about RelativeCI open source projects