Broken npm package
Some npm packages contain invalid JavaScript code.
See Workaround.
The reason why some npm packages contain invalid code is because the transpilation process of webpack-based frameworks, most notably Next.js, isn't strict and tolerates erroneous code: thus an npm package can work with Next.js while not work with Vite-based frameworks. You can learn more at Why.
The situation will improve and eventually be completely resolved as Vite-based frameworks gain popularity, which we can expect to happen quickly since all frameworks, except for Next.js, are using Vite nowadays.
Workaround
You can workaround problematic npm packages by using on of the following:
ssr.noExternal
vite-plugin-cjs-interop
- SSR opt-out
The workarounds usually work. But if you struggle working around a broken npm package then feel free to reach out for help.
ssr.noExternal
You may need to add nested npm packages to
ssr.noExternal
, because CJS/ESM issues sometimes cascade down along the "noExternal
boundary" as you add npm packages tossr.noExternal
.
The section Why explains why
ssr.noExternal
is a workaround.
vite-plugin-cjs-interop
See GitHub > cyco130/vite-plugin-cjs-interop
.
For the following errors, we recommend trying vite-plugin-cjs-interop
before trying ssr.noExternal
:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: something-else.
SyntaxError: Named export 'SomeImport' not found. The requested module 'some-library' is a CommonJS module, which may not support all module.exports as named exports.
SSR opt-out
As a last resort, if both ssr.noExternal
and vite-plugin-cjs-interop
doesn't work, you can opt out of SSR:
Opting out of SSR almost always works, see explanation at Why.
Why
You may ask yourself how it's possible that an npm package can publish invalid JavaScript code that doesn't get fixed for months.
The main reason is that some frameworks such as Next.js transpile the server-side code of npm packages, whereas Vite transpiles only the client-side code of npm packages. When server-side code contains invalid JavaScript then Node.js crashes and throws one of these errors, while transpilers are more tolerant and transform invalid JavaScript (that Node.js isn't able to execute) into valid JavaScript (that Node.js is able to execute).
By default, Vite doesn't transpile the server-side code of npm packages for a much faster DX, so that Node.js directly executes the server-side code without involving a slow transpilation process.
That's why adding an npm package to ssr.noExternal
is usually a workaround when the npm package contains invalid JavaScript.
By adding an npm package to ssr.noExternal
, you replicate the behavior of frameworks like Next.js.
The issue is most widespread in the React ecosystem because of Next.js's prevalence, but this is going to less and less of an issue as Vite-based React frameworks gain popularity. All frameworks other than Next.js are now Vite-based (e.g. Remix), thus the situation will quickly improve.
Common errors
Common invalid JavaScript code published by npm packages.
Cannot use import statement outside a module
(node:30335) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
node_modules/some-library/dist/esm/index.js:1
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1033:15)
at Module._compile (node:internal/modules/cjs/loader:1069:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Module._load (node:internal/modules/cjs/loader:827:12)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:170:29)
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:409:24)
Node.js v18.0.0
Node.js's message
set "type": "module" in package.json or use the .mjs extension
is misleading because it means the library'snode_modules/some-library/package.json
, not yourpackage.json
. It isn't really actionable (unless you patch the library).
Recommended workaround:
Alternatively, you can try to patch the broken npm package with:
Named export not found
import { SomeImport } from "some-library";
^^^^^^^^^^
SyntaxError: Named export 'SomeImport' not found. The requested module 'some-library' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'some-library';
const { SomeImport } = pkg;
at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)
Node.js v18.0.0
For default exports, the workaround proposed by Node.js may not work and you may need to do this instead:
For a workaround that is global (and TypeScript friendly), see vite-plugin-cjs-interop
.
ERR_MODULE_NOT_FOUND
node:internal/process/esm_loader:91
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'node_modules/some-library/dist/esm/some-file' imported from node_modules/some-library/dist/esm/index.js
Did you mean to import some-file.js?
at new NodeError (node:internal/errors:372:5)
at finalizeResolution (node:internal/modules/esm/resolve:405:11)
at moduleResolve (node:internal/modules/esm/resolve:966:10)
at defaultResolve (node:internal/modules/esm/resolve:1176:11)
at ESMLoader.resolve (node:internal/modules/esm/loader:605:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:318:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
at link (node:internal/modules/esm/module_job:78:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v18.0.0
The error is usually thrown when the library's ESM code contains
import './some-file'
. (It should beimport './some-file.js'
instead, as imports in ESM code are required to include the file extension.)
Recommended workaround: ssr.noExternal
.
ERR_UNSUPPORTED_DIR_IMPORT
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import 'node_modules/some-library/dist/some-dir/' is not supported resolving ES modules imported from node_modules/some-library/dist/index.js
Did you mean to import ./some-dir/index.js?
at finalizeResolution (node:internal/modules/esm/resolve:412:17)
ESM doesn't allow directory imports: all import paths need to point to a filename instead.
Recommended workaround: ssr.noExternal
.
ERR_UNKNOWN_FILE_EXTENSION .css
Another common problem is code importing CSS which makes Node.js crash:
Error: ERR_UNKNOWN_FILE_EXTENSION .css node_modules/some-library/dist/main.css
at someFunction (node_modules/some-library/dist/main.js)
at nextLoad (node:internal/modules/esm/loader:163:28)
at ESMLoader.load (node:internal/modules/esm/loader:605:26)
Recommended workaround: ssr.noExternal
.
Cannot read properties of undefined
The error Cannot read properties of undefined
is often caused by ESM/CJS issues.
TypeError: Cannot read properties of undefined (reading 'someProp')
at someFunction (node_modules/some-good-lib/dist/index.js:1000:3)
at someHook (renderer/+someHook.js:13:37)
The underlying issue is often this:
Sometimes, when dependency injection is used, the
import
tosome-broken-lib
isn't in the file in which the exception is being raised, making it harder to understand which library is broken. See here an example of this.
Adding some-broken-lib
to ssr.noExternal
usually solves the issue.
Alternatively, you can add some-good-lib
to ssr.noExternal
while adding some-broken-lib
to vite-plugin-cjs-interop
.
React invalid component
The following is a common React error and the root cause is usually a CJS/ESM issue.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
at renderElement (node_modules/react-dom/...)
at renderNodeDestructiveImpl (node_modules/react-dom/...)
at renderNodeDestructive (node_modules/react-dom/...)
...
Or got: object
.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
React usually logs a component trace before throwing the error:
Check your code at +Page.tsx:26.
at Page
at div
at div
at Layout (/pages/+Layout.tsx:66:19)
Sometimes React doesn't log a component trace. In that case you can use this temporary patch to get a component trace.
You can also use the temporary patch to get a more precise component trace. (For example the component trace above says
Check your code at +Page.tsx:26
but there can be hundreds of+Page.tsx
files.)
Use the component trace to find out which component is undefined
/ an object
. You'll likely see that the component is imported and that the import value is undefined
/ an object
(instead of a React component) because of CJS/ESM interoperability quirks.
A local workaround is usually this:
For a workaround that is global (and TypeScript friendly), see vite-plugin-cjs-interop
.