I have built an npm package, a reusable ReactJS component using Vite and its library mode. It worked until just over a week ago, when I tried running the local dev server to add some new features via a npm symlink. That's when it suddenly threw Invalid hook call. Hooks can only be called inside of the body of a function component...
:
As an initial guide for the library I used this post.
The described problems in the error
First thing I did was checking the three possible causes described in the error.
The general cause for the problem when reading similar situations seems to be the use of npm link or a symlink. But it seems even after removing it the error remains.
Checking an earlier git version
Since the current git version is not working, I tried to check out an earlier version. But the devtools still display the same error for that version.
Creating a new clean Vite project and installing my npm package
I have also created a separate new clean Vite project, where I have installed the npm package. It displays the error.
Looked through the thread suggestions
I have looked through many of the suggested solutions in the thread linked in this issue
resolutions
flag to package.jsonpeerDependencies
The main parts of the project are the lib, src and dist folders.
💡 Since I can implement the component in the src-folder via the relative path (from the lib-folder) without any problems. My best guess is that something with the configuration and dist-folder is causing the error.
package.json
{
"name": "react-sheet-modal",
"private": false,
"version": "1.0.11",
"description": "An authentic modal sheet for ReactJs - Bringing the ()iOS experience to the web",
"repository": {
"url": "git+https://github.com/Colin-Farkas-Personal/React-Sheet-Modal.git"
},
"homepage": "https://github.com/Colin-Farkas-Personal/React-Sheet-Modal/blob/main/README.md",
"keywords": [
"ReactJs",
"sheet",
"modal"
],
"author": {
"name": "Colin Farkas",
"email": "[email protected]"
},
"type": "module",
"main": "dist/main.js",
"module": "dist/main.js",
"types": "dist/main.d.ts",
"files": [
"dist"
],
"sideEffects": [
"**/*.css"
],
"scripts": {
"dev": "vite",
"dev:app1": "vite --mode app_1",
"dev:app2": "vite --mode app_2",
"build": "tsc -b ./tsconfig.lib.json && vite build",
"build:watch": "tsc -b ./tsconfig.lib.json && vite build --watch",
"lint": "eslint .",
"preview": "vite preview",
"prepublishOnly": "npm run build"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/node": "^22.9.3",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"concurrently": "^9.1.0",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.13",
"globals": "^15.11.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sass": "^1.83.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.10.0",
"vite": "^5.4.9",
"vite-plugin-dts": "^4.3.0",
"vite-plugin-lib-inject-css": "^2.1.1",
"vite-plugin-restart": "^0.4.2",
"vite-plugin-svgr": "^4.3.0"
}
}
vite.config.ts
import { resolve } from 'path';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import { libInjectCss } from 'vite-plugin-lib-inject-css';
import ViteRestart from 'vite-plugin-restart';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [
libInjectCss(),
dts({
tsconfigPath: resolve(__dirname, 'tsconfig.lib.json'),
}),
ViteRestart({
restart: ['/lib/**'],
}),
svgr({
svgrOptions: {
ref: true,
svgo: false,
titleProp: true,
},
include: '**/*.svg',
}),
],
build: {
copyPublicDir: false,
lib: {
entry: resolve(__dirname, 'lib/main.ts'),
formats: ['es'],
},
rollupOptions: {
output: {
assetFileNames: 'assets/[name][extname]',
entryFileNames: '[name].js',
},
external: ['react', 'react-dom'],
},
},
resolve: {
dedupe: ['react', 'react-dom'],
},
});
tsconfig.app.json
{
"compilerOptions": {
"target": "ES6",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"types": ["vite/client"]
},
"include": ["src", "env.ts", "src/vite-env.d.ts"]
}
tsconfig.lib.json
{
"extends": "./tsconfig.app.json",
"include": ["lib"],
"exclude": ["src"]
}
I was able to fix this error by ensuring that no instances of react/jsx-runtime were imported or bundled into the transpiled code.
The issue was that the main.js file (inside the dist folder) contained react/jsx-runtime, which caused duplicate React export objects since the development environment was also using React.
I updated the rollupOptions inside vite.config.ts to properly exclude the react/jsx-runtime code:
export default defineConfig({
{ ... }
build: {
copyPublicDir: false,
lib: {
entry: resolve(__dirname, './lib/main.ts'),
formats: ['es'],
},
rollupOptions: {
external: ['react', 'react/jsx-runtime'],
output: {
assetFileNames: 'assets/[name][extname]',
entryFileNames: '[name].js',
},
},
},
});