Skip to content

React error #130 when bundled by Vite 8 / rolldown: default import is the CJS namespace, not the Slider class #2444

@esetnik

Description

@esetnik

Description

When a React app with "type": "module" in its package.json is bundled by Vite 8 (which ships rolldown as its bundler), import Slider from 'react-slick' resolves to the CJS namespace object {default: Slider, __esModule: true} at runtime — not the Slider class. Any <Slider> render site then receives an object as its component type and throws:

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

(minified: React error #130; visit https://react.dev/errors/130?args[]=object&args[]=)

Root cause

lib/index.js ends with:

exports["default"] = void 0;
var _slider = _interopRequireDefault(require("./slider"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
var _default = exports["default"] = _slider["default"];

It sets exports.default and flags __esModule: true, but it never re-assigns module.exports = exports["default"]. When the consumer is flagged "type": "module", rolldown follows Node's ESM-for-CJS convention and treats the default import as module.exports itself, which is the whole namespace {default: Slider, __esModule: true} — not the class.

This is documented / intentional behavior on rolldown's side:

The de-facto workaround in the ecosystem is for the CJS package itself to re-export its default on module.exports — e.g. react-modal's own lib/index.js ends with:

module.exports = exports["default"];

So module.exports equals the class at runtime regardless of which CJS-default-import heuristic the bundler picks. react-slick does not do this, which is why it breaks specifically under Vite 8.

Reproduction

Minimal setup (any Vite 8 project with "type": "module" works):

// src/main.tsx
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import Slider from 'react-slick';
import { createRoot } from 'react-dom/client';

console.log('typeof Slider =', typeof Slider); // 'object' under vite 8 — should be 'function'

createRoot(document.getElementById('root')!).render(
  <Slider slidesToShow={1}>
    <div>slide 1</div>
    <div>slide 2</div>
  </Slider>
);
// package.json excerpt
{
  "type": "module",
  "dependencies": {
    "react": "19.2.5",
    "react-dom": "19.2.5",
    "react-slick": "0.31.0",
    "slick-carousel": "1.8.1",
    "vite": "8.0.8"
  }
}
$ pnpm build && pnpm preview
# open http://localhost:4173
# Console: Uncaught Error: Minified React error #130; ... args[]=object

Reproduces under Vite 8.0.x (rolldown). Works fine under Vite 7.x (pre-rolldown) and under webpack / Rollup / esbuild in every configuration I've tested.

Suggested fix

The compiled entry should end up with module.exports pointing at the Slider class directly, matching what react-modal's own compiled output does. Two equivalent ways to get there — PR #2445 uses the first:

1. Source-level (proposed in #2445) — in src/index.js:

 import Slider from "./slider";

-export default Slider;
+module.exports = Slider;
+module.exports.default = Slider;

After @babel/preset-env compiles, lib/index.js ends up as:

"use strict";
var _slider = _interopRequireDefault(require("./slider"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
module.exports = _slider["default"];
module.exports["default"] = _slider["default"];

No new dependency, no build-config change.

2. Babel-plugin alternative — keep src/index.js as export default Slider; and add babel-plugin-add-module-exports to .babelrc:

 "plugins": [
-    "@babel/plugin-proposal-class-properties"
+    "@babel/plugin-proposal-class-properties",
+    "add-module-exports"
 ]

That plugin is widely used for this exact interop fix but hasn't had a release since 2019, which is why I went with the source-level approach in #2445. Either works functionally.

Workaround for consumers today

For pnpm users:

// package.json
"pnpm": {
  "patchedDependencies": {
    "react-slick@0.31.0": "patches/react-slick@0.31.0.patch"
  }
}
# patches/react-slick@0.31.0.patch
diff --git a/lib/index.js b/lib/index.js
--- a/lib/index.js
+++ b/lib/index.js
@@ -7,3 +7,4 @@
 var _slider = _interopRequireDefault(require("./slider"));
 function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
 var _default = exports["default"] = _slider["default"];
+module.exports = exports["default"];

For npm/yarn users, the equivalent with patch-package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions