Skip to content

Vite 8 dev pre-bundler doesn't unwrap exports.default for CJS packages #22227

@sdbondi

Description

@sdbondi

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

  1. Install a CJS package that uses exports.default, e.g. react-qr-code or react-simple-code-editor
  2. Import it with a default import: import QRCode from "react-qr-code"
  3. Use it as a React component in JSX
  4. 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.
    
  5. 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;

Metadata

Metadata

Assignees

No one assigned

    Labels

    p3-minor-bugAn edge case that only affects very specific usage (priority)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions