packages/codemods/v5-0-0/25-plugin-peer-dependencies.md
react, react-dom, and antd to Peer DependenciesIn a pnpm monorepo, if a custom block plugin declares react or react-dom as direct dependencies (even with a range like ^18 or ^19), pnpm may install a second React instance in the store. The Lowdefy dev server pins its own React version, and when plugin components resolve to a different copy, two React context trees exist at runtime. This breaks:
ConfigProvider are invisible to plugin blocksThe fix is to declare react, react-dom, and antd as peer dependencies in plugin packages, and optionally use pnpm.overrides in the root package.json to force a single version across the workspace.
This migration targets package.json files in custom block plugin directories, not YAML configs.
plugins — scan package.json files in local block plugin directories.
react and react-dom from dependencies to peerDependenciesFor each custom block plugin package.json, remove react and react-dom from dependencies and add them to peerDependencies with an open range:
{
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
}
Do not pin a specific version (e.g., "18.2.0" or "^19.0.0"). The range >=18 lets the plugin work with whatever React version the Lowdefy dev server provides.
antd from dependencies to peerDependencies (if present)If the plugin imports from antd directly (e.g., import { Tag } from 'antd'), it should declare antd as a peer dependency:
{
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18",
"antd": ">=5"
}
}
If the plugin only imports from @lowdefy/blocks-antd or other @lowdefy/* packages (not antd itself), antd does not need to be listed.
Check which plugins import antd directly:
grep -rn "from 'antd'" plugins/*/src/ --include='*.js'
grep -rn "from \"antd\"" plugins/*/src/ --include='*.js'
pnpm.overrides to the root package.jsonIn the monorepo root package.json, add pnpm.overrides to force a single React version across all workspace packages:
{
"pnpm": {
"overrides": {
"react": "18.2.0",
"react-dom": "18.2.0"
}
}
}
This ensures that even if transitive dependencies pull in a different React version, pnpm collapses them all to one copy.
Choose the version that the Lowdefy dev server uses. As of Lowdefy v5, this is 18.2.0.
pnpm installRegenerate the lockfile to collapse duplicate installations:
pnpm install
Check that all plugin symlinks resolve to the same React copy:
readlink plugins/*/node_modules/react
All paths should point to the same [email protected] store entry.
Glob: plugins/*/package.json
Grep patterns:
"react": in dependencies — should only appear in peerDependencies for plugin packages"react-dom": in dependencies — same"antd": in dependencies — move to peerDependencies if plugin imports antd directlyAlso check non-plugin packages that might pull in a competing React version:
grep -rn '"react":' */package.json --include='package.json'
If other non-plugin packages in the workspace have React as a direct dependency, consider aligning those versions too, or rely on pnpm.overrides to force alignment.
package.json{
"name": "@my-org/plugin-blocks",
"dependencies": {
"@lowdefy/block-utils": "5.0.0",
"@lowdefy/helpers": "5.0.0",
"antd": "4.22.5",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@swc/cli": "0.1.57",
"@swc/core": "1.2.194"
}
}
package.json{
"name": "@my-org/plugin-blocks",
"dependencies": {
"@lowdefy/block-utils": "5.0.0",
"@lowdefy/helpers": "5.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18",
"antd": ">=5"
},
"devDependencies": {
"@swc/cli": "0.1.57",
"@swc/core": "1.2.194"
}
}
package.json (no overrides){
"name": "my-lowdefy-app",
"private": true,
"packageManager": "[email protected]"
}
package.json (with overrides){
"name": "my-lowdefy-app",
"private": true,
"packageManager": "[email protected]",
"pnpm": {
"overrides": {
"react": "18.2.0",
"react-dom": "18.2.0"
}
}
}
react and react-dom as direct dependencies. The pnpm.overrides in the root ensures they still use the same version.react or react-dom at all — neither as dependencies nor peer dependencies. Only block plugins need this migration.@monaco-editor/react and similar: Libraries that have react as a peer dependency themselves work correctly when the plugin also uses peer dependencies — pnpm resolves them all to the same overridden version.pnpm.overrides will force alignment. Verify the library accepts the pinned version before upgrading.pnpm.overrides version. The plugin peer dep ranges (>=18) will continue to work without changes.antd version: In Lowdefy v5, antd is v6.x. Plugins that import antd directly should use "antd": ">=5" as the peer dep range (not >=4) to prevent accidentally resolving to antd v4 which has incompatible APIs.No plugin should have react or react-dom in dependencies:
# Should return no matches in plugin package.json files
grep -A1 '"dependencies"' plugins/*/package.json | grep '"react"'
All plugins with block exports should have react and react-dom in peerDependencies:
grep -A3 '"peerDependencies"' plugins/*/package.json | grep '"react"'
Root package.json should have pnpm.overrides:
grep -A4 '"overrides"' package.json
After pnpm install, all plugins resolve to the same React version:
readlink plugins/*/node_modules/react
Build each plugin and start the dev server — blocks should inherit the antd theme (dark mode toggle, compact algorithm, design tokens)