Description
Vite 8's dev server pre-bundler (oxc/rolldown) doesn't properly unwrap exports.default for CJS packages. When a CJS package sets exports.default = SomeComponent, the pre-bundled output ends with:
export default require_lib();
This returns the whole CJS module object { default: Component } instead of unwrapping to the component itself. As a result, import Foo from "cjs-package" resolves to the module wrapper object rather than the actual default export, causing runtime crashes.
Production builds (rolldown) handle this correctly - the issue is only in dev mode pre-bundling.
Reproduction
- Install a CJS package that uses
exports.default, e.g. react-qr-code or react-simple-code-editor
- Import it with a default import:
import QRCode from "react-qr-code"
- Use it as a React component in JSX
- Run
vite dev - crashes at runtime with:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
- Run
vite build - works correctly
Minimum Reproduction: https://github.com/sdbondi/vite8-cjs-interop-repro
Expected behavior
import Foo from "cjs-package" should resolve to the value of exports.default in dev mode, matching the production build behavior.
Actual behavior
The import resolves to the CJS module wrapper object { default: Component, ... } instead of the unwrapped default export.
Pre-bundled output
Looking at node_modules/.vite/deps/react-qr-code.js, the tail of the file shows:
exports.QRCode = QRCode;
exports.default = QRCode;
}));
//#endregion
export default require_lib();
require_lib() returns the whole exports object rather than unwrapping .default.
System Info
- Vite 8.0.8
- Node 22
- Linux
- pnpm
Workaround
For packages that also have a named export (e.g. react-qr-code exports both exports.default and exports.QRCode), use the named import:
// @ts-expect-error CJS interop: Vite 8 dev pre-bundler doesn't unwrap exports.default
import { QRCode } from "react-qr-code";
For packages that only have exports.default (e.g. react-simple-code-editor), use a runtime fallback:
import EditorImport from "react-simple-code-editor";
const Editor = (EditorImport as any).default ?? EditorImport;
Description
Vite 8's dev server pre-bundler (oxc/rolldown) doesn't properly unwrap
exports.defaultfor CJS packages. When a CJS package setsexports.default = SomeComponent, the pre-bundled output ends with:This returns the whole CJS module object
{ default: Component }instead of unwrapping to the component itself. As a result,import Foo from "cjs-package"resolves to the module wrapper object rather than the actual default export, causing runtime crashes.Production builds (rolldown) handle this correctly - the issue is only in dev mode pre-bundling.
Reproduction
exports.default, e.g.react-qr-codeorreact-simple-code-editorimport QRCode from "react-qr-code"vite dev- crashes at runtime with:vite build- works correctlyMinimum Reproduction: https://github.com/sdbondi/vite8-cjs-interop-repro
Expected behavior
import Foo from "cjs-package"should resolve to the value ofexports.defaultin dev mode, matching the production build behavior.Actual behavior
The import resolves to the CJS module wrapper object
{ default: Component, ... }instead of the unwrapped default export.Pre-bundled output
Looking at
node_modules/.vite/deps/react-qr-code.js, the tail of the file shows:require_lib()returns the whole exports object rather than unwrapping.default.System Info
Workaround
For packages that also have a named export (e.g.
react-qr-codeexports bothexports.defaultandexports.QRCode), use the named import:For packages that only have
exports.default(e.g.react-simple-code-editor), use a runtime fallback: