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:
# 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.
Description
When a React app with
"type": "module"in itspackage.jsonis 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 theSliderclass. Any<Slider>render site then receives an object as its component type and throws:(minified:
React error #130; visit https://react.dev/errors/130?args[]=object&args[]=)Root cause
lib/index.jsends with:It sets
exports.defaultand flags__esModule: true, but it never re-assignsmodule.exports = exports["default"]. When the consumer is flagged"type": "module", rolldown follows Node's ESM-for-CJS convention and treats the default import asmodule.exportsitself, 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 ownlib/index.jsends with:So
module.exportsequals the class at runtime regardless of which CJS-default-import heuristic the bundler picks.react-slickdoes not do this, which is why it breaks specifically under Vite 8.Reproduction
Minimal setup (any Vite 8 project with
"type": "module"works):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.exportspointing at theSliderclass directly, matching whatreact-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:After
@babel/preset-envcompiles,lib/index.jsends up as:No new dependency, no build-config change.
2. Babel-plugin alternative — keep
src/index.jsasexport default Slider;and addbabel-plugin-add-module-exportsto.babelrc: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:
For npm/yarn users, the equivalent with
patch-package.