Mahdi BEN RHOUMAVite build fails with 'Rollup failed to resolve import '/src/main.tsx''. The root cause is an index.html inside src and a custom root config. Fix it by moving index.html to the project root.
If you're migrating a React app from Webpack to Vite and the build fails with [vite]: Rollup failed to resolve import "/src/main.tsx" from your index.html, the problem is almost always the placement of the index.html file relative to the root configured in vite.config.ts. Fix it by moving index.html to the project root and removing the custom root option. This exact error was reported in a popular Stack Overflow question where a Webpack‑to‑Vite migration produced the same diagnostic.
[vite]: Rollup failed to resolve import "/src/main.tsx" during vite build.index.html is inside a custom root directory (e.g., src/) and its script tag uses an absolute path that starts from the project‑level directory, not from the configured root.index.html to the project root and remove the root property from vite.config.ts.npm run build; the build completes without resolution errors.
The full terminal output you see looks like this:
> tsc && vite build
vite v4.3.5 building for production...
✓ 2 modules transformed.
✓ built in 32ms
[vite]: Rollup failed to resolve import "/src/main.tsx" from "/Users/John/source/reddwarf/frontend/snap-web/src/index.html".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
error during build:
Error: [vite]: Rollup failed to resolve import "/src/main.tsx" from "/Users/John/source/reddwarf/frontend/snap-web/src/index.html".
at viteWarn (...)
at onwarn (...)
at onRollupWarning (...)
...
It happens when you run vite build. The dev server (vite dev) often does not reproduce this error because its module resolution is more lenient — it can resolve some paths that the production Rollup build rejects. This is a common gotcha for developers who test everything with vite dev and only hit resolution failures during a build.
The error suggests adding the import to build.rollupOptions.external. Don’t do that. Externalising your own entry module will strip it from the bundle and cause runtime failures — the suggestion is a generic fallback, not the actual fix for this structural issue.
Vite uses the root property in vite.config.ts to determine where the project begins. The default root is the directory where you run vite, usually the project root (my-app/). In a default Vite scaffold, the index.html lives there and contains:
<script type="module" src="/src/main.tsx"></script>
The path /src/main.tsx is an absolute path from the project root — not relative to the HTML file, and not relative to the configured root. It says: “starting from the root of the filesystem as Vite sees it, go to src/main.tsx.”
In the failing setup, the developer moved index.html inside src/ and then set root: path.join(__dirname, 'src') in vite.config.ts. The intent was to mimic the Webpack structure where the entry HTML sits inside src. But now Vite’s root is src, and the script tag still says /src/main.tsx. To Vite, this means “start at the root (which is src/), then go to src/main.tsx” — so it looks for a file at <project>/src/src/main.tsx. That file doesn’t exist, and Rollup throws the resolution error.
The relevant problematic config:
// vite.config.ts — the broken version
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
root: path.join(__dirname, 'src'), // <— root becomes <project>/src
plugins: [react()],
build: {
outDir: "build"
}
});
With that root, the script tag’s /src/main.tsx tries to resolve to <project>/src/src/main.tsx and the build fails.
The most straightforward fix — and what the official Vite documentation expects — is to move index.html back to the project root and remove the custom root property.
Before you move files, check your current layout. Run tree -L 2 (install it via brew install tree on macOS if not present) from the project directory. The broken structure looks like this:
.
├── node_modules
├── package.json
├── src
│ ├── App.tsx
│ ├── main.tsx
│ ├── index.html # <— index.html inside src
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts # contains root: 'src'
Your index.html inside src might reference the entry point with an absolute path like <script type="module" src="/src/main.tsx"></script>. That path is relative to the project root (the directory containing vite.config.ts), but with root set to src, it becomes src\src\main.tsx and causes the failure.
Move the index.html to the project root:
mv src/index.html index.html
Now your tree should show:
.
├── index.html # <— now at project root
├── node_modules
├── package.json
├── src
│ ├── App.tsx
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts # no root property
The index.html now sits next to vite.config.ts. Vite automatically treats the directory containing vite.config.ts as the root, so the absolute path /src/main.tsx correctly resolves to <project>/src/main.tsx.
Some developers attempt a workaround that does not involve moving index.html: they keep root: 'src' and change the script tag to "./main.tsx" (relative to the HTML file). This lets Vite resolve the file because root is src, and ./main.tsx correctly points to src/main.tsx. While this clears the resolution error, it introduces fragility. The Vite documentation strongly advises keeping index.html at the project root. Moving it to a subdirectory and changing to relative paths can break imports of other assets (like favicon or static files) that still rely on absolute paths from the root. Stick to the recommended layout: index.html at the project root, no custom root in the config.
The config file after the fix:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: "build" // keep your desired output dir
}
});
Notice that the root property is completely removed. With this setup, vite build will resolve /src/main.tsx relative to the project directory, just as it should.
After moving index.html and removing the root property, run the build again:
npm run build
A successful build will output something like:
vite v4.3.5 building for production...
✓ 32 modules transformed.
build/index.html 0.46 kB
build/assets/index-abc123.js 145.32 kB │ gzip: 48.76 kB
build/assets/index-abc123.css 0.87 kB │ gzip: 0.44 kB
✓ built in 1.23s
No more Rollup resolution errors. The HTML, JavaScript, and CSS are written to the build/ directory (or whatever you set as build.outDir). Open build/index.html and verify that the <script> tag points to the hashed asset file. That file is your bundled application entry.
If you still see the same resolution error, double‑check the file placement with ls:
ls -l src/main.tsx # should exist
ls -l index.html # must be at the project root
Also inspect your vite.config.ts to make sure no root property remains. A leftover root: 'src' will cause the error to reappear even after moving the file, because Vite will once again resolve the absolute /src/main.tsx path starting from src/.
If the build fails for a different reason — for instance, a TypeScript type error or a missing dependency — address that separately. The resolution error should be gone. If it persists, check that your package.json build script doesn't override the config or pass an explicit root via a CLI flag like vite build --root src. That would reintroduce the problem.
If you’re using WebStorm or IntelliJ IDEA, the Node.js interpreter configured in the IDE can mask or multiply build problems. In the Stack Overflow case, the developer had an outdated Node 14 interpreter set for the project while nvm was using Node 18. This mismatch caused ESLint and the TypeScript service to fail with extraneous errors like:
Error: Cannot find module 'eslint-plugin-react'
Error: Cannot find module 'typescript'
These errors appeared in the IDE’s terminal and tool window alongside the Rollup failure, making it harder to isolate the real issue.
Verify your active Node version outside the IDE:
node -v # e.g., v18.17.1
which node # e.g., /Users/yourname/.nvm/versions/node/v18.17.1/bin/node
In WebStorm, open Settings → Languages & Frameworks → Node.js. In the Node interpreter field, click the browse button and select the path reported by which node. Alternatively, if you use nvm, you can choose the .nvm directory’s interpreter. Apply the change and restart the ESLint/TypeScript services (usually automatically after changing the interpreter). The spurious module‑not‑found errors should disappear.
Once the IDE interpreter matches the CLI version, the only error you’ll see when running vite build is the Rollup resolution failure. Fixing that (moving index.html) then lets the build succeed cleanly inside the IDE as well.
vite build and not during vite dev?
Vite’s development server uses esbuild’s resolution logic, which is more forgiving than Rollup’s. It can sometimes resolve paths that are technically incorrect. The production build, however, uses Rollup for bundling and enforces strict resolution — that’s when the mismatch becomes a hard error.
build.rollupOptions.external as the error suggests?
No. External elements are not bundled; they are expected to be provided at runtime (e.g., a global variable). Adding "/src/main.tsx" to external would cause the build to complete, but your bundle would be missing the application entry point and break in the browser.
Originally published at https://www.iloveblogs.blog