react-easy-crop: Cannot use import statement outside a module

Describe the bug I’m getting Cannot use import statement outside a module when building it for prod, dev just works fine. I’ve type: module in my package.json and here’s my tsconfig

"esModuleInterop": true,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",

And when importing Cropper like this import Cropper from 'react-easy-crop' gives this error when using it

JSX element type 'Cropper' does not have any construct or call signatures.ts(2604)

This project did build successfully before with these exact configs but after adding react-easy-crop, it doesn’t

Full error:

(node:406) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
SyntaxError: Cannot use import statement outside a module
/myapp/node_modules/react-easy-crop/index.module.js:1
import { __assign, __extends } from 'tslib';
^^^^^^
SyntaxError: Cannot use import statement outside a module

Expected behavior It should build successfully with these configs and shouldn’t throw an error when using the Cropper component

About this issue

  • Original URL
  • State: open
  • Created 9 months ago
  • Reactions: 1
  • Comments: 26 (12 by maintainers)

Most upvoted comments

You have a misconfiguration in the package.json. Please consider fixing this small bug that makes the library hard to use with some frameworks.

Please have a look at the lint error/warnings for the library here https://publint.dev/react-easy-crop@5.0.6 and here https://arethetypeswrong.github.io/?p=react-easy-crop%405.0.6

./index.module.js is written in ESM, but is interpreted as CJS. Consider using the .mjs extension, e.g. ./index.module.mjs ([More info](https://publint.dev/rules.html#file_invalid_format)) The types is not exported. Consider adding pkg.exports["."].import.types to be compatible with TypeScript's "moduleResolution": "bundler" compiler option. Note that you cannot use "./index.d.ts" because it has a mismatching format. Instead, you can duplicate the file and use the .mts extension, e.g. pkg.exports["."].import.types: "./index.d.mts" ([More info](https://publint.dev/rules.html#types_not_exported))

[Resolution failed](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md) Import failed to resolve to type declarations or JavaScript files.

For anyone using Remix V2 with Vite you can fix this bug by adding the ssr.noExternal option in the vite.config.js (https://remix.run/docs/en/main/future/vite#esm--cjs):

import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import path from "path"
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

installGlobals();

export default defineConfig({
  ssr: {
    noExternal: ['react-easy-crop', 'tslib']
  },
  plugins: [
    remix(),
  ],
  resolve: {
    alias: {
      "~": path.resolve(__dirname, "./app")
    }
  }
});

Oh sorry, I didn’t communicate clearly. The part of this issue is fixed. The one that would give an error in the code editor is fixed, but not the terminal error when starting the app in prod mode. @lww’s suggestion is correct here, .mjs extension should be used instead of .js

nvm, sorry, I tried the canary 5.0.7--canary.527.82deecf.0 with ssr.noExternal in the vite config. Without ssr.noExternal in the vite config, it fails on start. Failing with react-easy-crop@5.0.7--canary.527.1aad632.0 too 😞 Sorry dude, I know it’s a hard problem. Thanks for trying to figure this out, I don’t have anything else to suggest here.

I tried that but https://arethetypeswrong.github.io/?p=react-easy-crop%405.0.7--canary.527.82deecf.0 is even worse.

Can you try react-easy-crop@5.0.7–canary.527.82deecf.0 please?

I’m actually wondering if we should just get rid of tslib. I don’t even recall why it was added in the first place and it’s something I haven’t seen used elsewhere.

Could you please try 5.0.7--canary.2940c18.0 and let me know if it solves your issues? (I’m not really confident that it does)

Ah, I looked at the wrong dist file.

It’s not just my use case, it doesn’t work with Remix at all, here’s a minimal application of remix and react-easy-crop: https://stackblitz.com/edit/remix-run-remix-mryppq?file=app%2Froutes%2F_index.tsx

temp fix with patch-package:

diff --git a/node_modules/react-easy-crop/index.module.js b/node_modules/react-easy-crop/index.module.js
index 11cf90c..f50b7dd 100644
--- a/node_modules/react-easy-crop/index.module.js
+++ b/node_modules/react-easy-crop/index.module.js
@@ -1,7 +1,30 @@
-import { __assign, __extends } from 'tslib';
 import React from 'react';
 import normalizeWheel from 'normalize-wheel';
 
+function extendStatics(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+function __assign () {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    }
+    return __assign.apply(this, arguments);
+}
+
 /**
  * Compute the dimension of the crop area based on media size,
  * aspect ratio and optionally rotation
diff --git a/node_modules/react-easy-crop/package.json b/node_modules/react-easy-crop/package.json
index c807ad1..059d843 100644
--- a/node_modules/react-easy-crop/package.json
+++ b/node_modules/react-easy-crop/package.json
@@ -1,6 +1,7 @@
 {
   "name": "react-easy-crop",
   "version": "5.0.2",
+  "type": "module",
   "description": "A React component to crop images/videos with easy interactions",
   "homepage": "https://ValentinH.github.io/react-easy-crop/",
   "keywords": [

Essentially what is changes is that I added type: module to the package.json and then removed the import from tslib in index.module.js and declared the relevant functions in the file itself. This fix works for me and my use case, I HAVEN’T tested every function and every way you can use react-easy-crop, so test this solution for your use case