reactjsvite

Library built with React 18 doesn't work on React 19


Edit: Maybe you can tell me in the comments why you hate my question so much :D. I think it is a valid problem that others might have as well so I'll keep it.

I'm trying to build a node package.

Environment: vite v6 + React 18/19.

Added the configurations at the end of the question. When I build the project with React 19 it only works on React 19 projects and when I build it with React 18, it works on React 18 projects but not React 19. There is no breaking change or compatibility issue because I can run the project on both versions in dev environment. The issue arises only when I install it as a npm package on other projects. I externalized react and react-dom. In the project where the library is installed, I checked the imported React object used in the library and in the project itself and they both have the same reference (React1 === React2) but I still get this error when I build with version 19:

Uncaught TypeError: ReactSharedInternals is undefined

By React 18 and trying to run it on React 19 I get the following error:

undefined (reading 'ReactCurrentDispatcher')

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from "path";

export default defineConfig({
  plugins: [react()],
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'app',
      formats: ['es', 'umd', 'cjs']
    },
    rollupOptions: {
      external: [
        'react',
        'react-dom',
        '@mui/material',
      ],
    },
    sourcemap: false,
    minify: false
  }
})

package.json

{
  "name": "app",
  "version": "1.1.17",
  "type": "module",
  "main": "dist/app.umd.js",
  "module": "dist/app.es.js",
  "types": "dist/index.d.ts",
  "exports": {
    "import": "./dist/app.es.js",
    "require": "./dist/app.cjs.js"
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build && tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --declarationDir dist",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
  },
  "devDependencies": {
    "@emotion/styled": "^11.13.5",
    "@eslint/js": "^9.15.0",
    "@mui/material": "^6.1.9",
    "@types/node": "^22.10.1",
    "@types/react": "^19.1.0",
    "@types/react-dom": "^18.3.1",
    "@vitejs/plugin-react-swc": "^3.8.1",
    "eslint": "^9.15.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.19",
    "globals": "^15.12.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "typescript": "~5.6.2",
    "typescript-eslint": "^8.15.0",
    "vite": "^6.2.5"
  },
  "keywords": [
    "react",
    "mui",
    "snackbar",
    "provider",
    "hook"
  ],
  "peerDependencies": {
    "@emotion/styled": "*",
    "@mui/material": "*",
    "react": ">=18.*.*",
    "react-dom": ">=18.*.*"
  },
  "sideEffects": false
}

Solution

  • I had to exclude the plugin in the production environment in vite.config.ts :

    plugins: [mode === "development" ? react() : undefined],
    

    and also externalize mui with regex:

    external: [
            'react',
            'react-dom',
            /@mui\/material/,
          ]