Ah — now I understand the nuance in your question. You're using Vite's native index.html
handling, and yes:
When you run:
npm run build
And your index.html
contains:
<script type="module" src="/src/main.jsx"></script>
Vite does transform the index.html
during build, replacing that src
path with the final, hashed file name — like:
<script type="module" src="/assets/index.abcd1234.js"></script>
This is because Vite uses index.html
as part of its HTML entry build pipeline.
If you're doing two separate builds (e.g., modern + legacy) using different vite.config.js
files, Vite processes index.html
independently for each. That means:
- The second build will overwrite or regenerate
index.html
, based on its own config. - Vite does not merge the outputs or coordinate
script type="module"
andnomodule
.
- Run modern build (creates
/dist/module/index.html
with hashed modern script) - Run legacy build (creates
/dist/nomodule/main.js
, does not modifyindex.html
) - Then inject both final filenames (module + nomodule) into a clean final
index.html
Use a custom script like:
const fs = require('fs');
const path = require('path');
const moduleDir = 'dist/module/assets';
const nomoduleDir = 'dist/nomodule';
const moduleScript = fs.readdirSync(moduleDir).find(f => f.endsWith('.js'));
const nomoduleScript = fs.readdirSync(nomoduleDir).find(f => f.endsWith('.js'));
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Dual Build</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/assets/${moduleScript}"></script>
<script nomodule src="/nomodule/${nomoduleScript}"></script>
</body>
</html>
`;
fs.writeFileSync('dist/index.html', html);
✅ Option 2: Use vite-plugin-legacy
If you don’t want to manually manage dual builds, you can use Vite’s official plugin:
npm install --save-dev @vitejs/plugin-legacy
Then in vite.config.js
:
import legacy from '@vitejs/plugin-legacy';
export default {
plugins: [
react(),
legacy({
targets: ['defaults', 'not IE 11'], // or ['defaults', 'ie >= 11']
modernPolyfills: true,
renderLegacyChunks: true,
polyfills: true,
})
]
};
This plugin:
- Creates both module and nomodule builds
- Automatically injects both
<script type="module">
and<script nomodule>
intoindex.html
- Handles polyfills
🟢 This is the simplest way to get dual builds with correct script tags — no post-processing needed.
Use Case | Best Approach |
---|---|
Manual dual builds | Use post-processing script |
Prefer automatic handling | Use @vitejs/plugin-legacy |
Let me know if you want help wiring up either approach in your existing project.