React 19 compatibility
Summary
When using @mcp-ui/client in Goose (React 19 with Electron + Vite), we hit a runtime error about a mismatched React element/runtime. This usually happens when a dependency ships its own copy of React or react/jsx-runtime, or when multiple React versions end up installed. One way to make mcp-ui work smoothly across apps is to treat React as a peerDependency and keep it external to the bundle (including react/jsx-runtime), so the host app provides a single consistent React.
Error
A React Element from an older version of React was rendered. This is not supported. It can happen if:
- Multiple copies of the "react" package is used.
- A library pre-bundled an old copy of "react" or "react/jsx-runtime".
- A compiler tries to "inline" JSX instead of using the runtime.
Context
- Host: Electron + Vite
- React: 19.x in the host
- Affected package:
@mcp-ui/client (and potentially other @mcp-ui/* packages)
- We already alias/dedupe
react and react-dom in Vite; the issue surfaced because react/jsx-runtime was not also aliased/externalized.
Our host workaround (Vite) to force a single React runtime everywhere:
// vite.renderer.config.mts
resolve: {
alias: {
react: resolve(__dirname, 'node_modules/react'),
'react-dom': resolve(__dirname, 'node_modules/react-dom'),
'react/jsx-runtime': resolve(__dirname, 'node_modules/react/jsx-runtime.js'),
'react/jsx-dev-runtime': resolve(__dirname, 'node_modules/react/jsx-dev-runtime.js'),
},
dedupe: ['react', 'react-dom'],
}
Reference (host config): https://github.com/block/goose/blob/2e2369f8b61f60aad2c04b739e1a2fdc6547d266/ui/desktop/vite.renderer.config.mts
Possible solutions
-
Make React peer dependencies (not dependencies)
- In each published package:
peerDependencies:
"react": "^18 || ^19"
"react-dom": "^18 || ^19"
- Keep
react/react-dom in devDependencies for local dev/test, but not in dependencies.
-
Externalize React and the JSX runtime in the build
- Ensure the bundler does not include React or JSX runtime in published artifacts:
external: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime']
-
Validate against React 18 and 19
- Add a CI matrix or test locally against both versions.
- Update types/TS config as needed for React 19 (e.g.,
@types/react, @types/react-dom if used).
Outcome
- Consumers control the React version (standard practice).
- Avoids duplicate/mismatched React instances and the runtime error above.
- Reduces need for brittle alias workarounds in host apps.
React 19 compatibility
Summary
When using
@mcp-ui/clientin Goose (React 19 with Electron + Vite), we hit a runtime error about a mismatched React element/runtime. This usually happens when a dependency ships its own copy of React orreact/jsx-runtime, or when multiple React versions end up installed. One way to makemcp-uiwork smoothly across apps is to treat React as a peerDependency and keep it external to the bundle (includingreact/jsx-runtime), so the host app provides a single consistent React.Error
Context
@mcp-ui/client(and potentially other@mcp-ui/*packages)reactandreact-domin Vite; the issue surfaced becausereact/jsx-runtimewas not also aliased/externalized.Our host workaround (Vite) to force a single React runtime everywhere:
Reference (host config): https://github.com/block/goose/blob/2e2369f8b61f60aad2c04b739e1a2fdc6547d266/ui/desktop/vite.renderer.config.mts
Possible solutions
Make React peer dependencies (not dependencies)
peerDependencies:"react": "^18 || ^19""react-dom": "^18 || ^19"react/react-domindevDependenciesfor local dev/test, but not independencies.Externalize React and the JSX runtime in the build
external: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime']Validate against React 18 and 19
@types/react,@types/react-domif used).Outcome