react-19-plugin-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMigrate Grafana Plugin to React 19
将Grafana插件迁移至React 19
Grafana 13 (April 2026) moves from React 18 to React 19. Incompatible plugins will break.
Do not upgrade React to 19 — only make forward-compatible changes.
All changes go in one PR. Execute steps in order. Never manually edit .
yarn.lockGrafana 13(2026年4月)将从React 18升级到React 19。不兼容的插件将会失效。
请勿直接将React升级到19 —— 仅需做向前兼容的修改。
所有修改需合并到一个PR中。按顺序执行步骤。禁止手动编辑。
yarn.lockStep 1: Detect plugin context
步骤1:检测插件上下文
bash
PLUGIN_JSON=$([ -f src/plugin.json ] && echo "src/plugin.json" \
|| ([ -f plugin/src/plugin.json ] && echo "plugin/src/plugin.json" || echo ""))
PKG_JSON=$([ -f package.json ] && echo "package.json" \
|| ([ -f plugin/package.json ] && echo "plugin/package.json" || echo ""))
PLUGIN_ID=$(jq -r '.id' $PLUGIN_JSON 2>/dev/null)
[ -f yarn.lock ] && PM="yarn" || ([ -f pnpm-lock.yaml ] && PM="pnpm" || PM="npm")
CP_VERSION=$(jq -r '.version' .config/.cprc.json 2>/dev/null)
echo "PLUGIN_ID=$PLUGIN_ID PM=$PM CP=$CP_VERSION"If is empty, ask the user for the plugin root path.
PLUGIN_IDbash
PLUGIN_JSON=$([ -f src/plugin.json ] && echo "src/plugin.json" \
|| ([ -f plugin/src/plugin.json ] && echo "plugin/src/plugin.json" || echo ""))
PKG_JSON=$([ -f package.json ] && echo "package.json" \
|| ([ -f plugin/package.json ] && echo "plugin/package.json" || echo ""))
PLUGIN_ID=$(jq -r '.id' $PLUGIN_JSON 2>/dev/null)
[ -f yarn.lock ] && PM="yarn" || ([ -f pnpm-lock.yaml ] && PM="pnpm" || PM="npm")
CP_VERSION=$(jq -r '.version' .config/.cprc.json 2>/dev/null)
echo "PLUGIN_ID=$PLUGIN_ID PM=$PM CP=$CP_VERSION"如果为空,请询问用户插件的根路径。
PLUGIN_IDStep 2: Scan for compatibility issues
步骤2:扫描兼容性问题
Build the plugin and run the React 19 compatibility scanner:
bash
npm run build 2>&1 | tail -5
npx -y @grafana/react-detect@latest 2>&1Save the output. It flags:
- /
jsxRuntimeImport→ Step 4 fixes this__SECRET_INTERNALS - /
defaultProps/propTypes→ Step 8 (source fixes)ReactDOM.render - → Step 6 (dependency bump) or Step 8 (source fix)
findDOMNode
If the build fails (plugin hasn't been built before), skip this step and run react-detect
after Step 9 instead. If output says "No breaking changes detected", still proceed — jsx-runtime
externalization and grafanaDependency bump are always required.
Re-run react-detect after Step 9 to confirm all issues are resolved.
构建插件并运行React 19兼容性扫描工具:
bash
npm run build 2>&1 | tail -5
npx -y @grafana/react-detect@latest 2>&1保存输出结果。它会标记以下问题:
- /
jsxRuntimeImport→ 步骤4可修复此问题__SECRET_INTERNALS - /
defaultProps/propTypes→ 步骤8(源码修复)ReactDOM.render - → 步骤6(依赖版本提升)或步骤8(源码修复)
findDOMNode
如果构建失败(插件从未构建过),跳过此步骤,在步骤9之后再运行react-detect。如果输出显示“No breaking changes detected”,仍需继续执行——jsx-runtime外部化和grafanaDependency版本提升是必须完成的操作。
在步骤9之后重新运行react-detect,确认所有问题已解决。
Step 3: Update @grafana/create-plugin
@grafana/create-plugin步骤3:更新@grafana/create-plugin
@grafana/create-pluginThe scaffolding update brings in externals extraction, jest mocks, Docker fixes, and webpack
improvements needed for React 19. Always do this before .
add externalize-jsx-runtimeRequires a clean git working tree. Create a feature branch first if not already on one.
脚手架更新会引入React 19所需的外部依赖提取、jest模拟、Docker修复和webpack改进。必须在执行之前完成此步骤。
add externalize-jsx-runtime需要干净的git工作区。如果尚未创建特性分支,请先创建。
Run the update
运行更新命令
bash
npx @grafana/create-plugin@latest update 2>&1bash
npx @grafana/create-plugin@latest update 2>&1If yarn install
fails with "engine is incompatible"
yarn install如果yarn install
因“engine is incompatible”失败
yarn installThe update runs an intermediate without . Complete it manually:
yarn install--ignore-enginesbash
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10Commit the intermediate state and re-run:
bash
git add -A && git commit -m "chore: intermediate create-plugin update" --no-verify
npx @grafana/create-plugin@latest update 2>&1更新过程会在未添加参数的情况下执行中间步骤。请手动完成:
--ignore-enginesyarn installbash
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10提交中间状态后重新运行:
bash
git add -A && git commit -m "chore: intermediate create-plugin update" --no-verify
npx @grafana/create-plugin@latest update 2>&1If ESLint 9 migration (004) fails with a parser error
如果ESLint 9迁移(004)因解析器错误失败
The auto-migration can generate invalid JS on plugins with complex ESLint configs.
Do not skip — commit what succeeded, then complete the ESLint 9 migration manually:
bash
git add -A && git commit -m "chore: update create-plugin (ESLint 9 migration manual)" --no-verifyThen follow the "Complete ESLint 9 migration" section below to finish.
自动迁移在配置复杂的插件上可能生成无效JS代码。请勿跳过——提交已成功的部分,然后手动完成ESLint 9迁移:
bash
git add -A && git commit -m "chore: update create-plugin (ESLint 9 migration manual)" --no-verify然后按照下方“完成ESLint 9迁移”部分的说明完成操作。
After the update
更新完成后
Always run install and verify:
bash
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10
cat .config/.cprc.jsonCommit if there are changes:
bash
git add -A && git diff --cached --quiet || git commit -m "chore: update create-plugin scaffolding" --no-verify务必执行安装并验证:
bash
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10
cat .config/.cprc.json如有变更则提交:
bash
git add -A && git diff --cached --quiet || git commit -m "chore: update create-plugin scaffolding" --no-verifyStep 3b: Complete ESLint 9 migration
步骤3b:完成ESLint 9迁移
The bumps ESLint to v9, which requires flat config ()
instead of . Whether the auto-migration (004) succeeded, partially succeeded, or
failed, you must ensure ESLint works before proceeding.
create-plugin updateeslint.config.js.eslintrccreate-plugin updateeslint.config.js.eslintrcCheck the current state
检查当前状态
bash
ls eslint.config.js .eslintrc* .config/.eslintrc* 2>/dev/null
npx eslint --version 2>&1Three scenarios:
A) exists and passes — auto-migration succeeded. Proceed.
eslint.config.jsyarn lintB) exists but fails — partial migration. Fix the issues:
eslint.config.jsyarn lintbash
yarn lint 2>&1 | head -30Common fixes:
- or
Invalid option '--ignore-path'→ remove those flags from theInvalid option '--ext'script inlint. In ESLint v9 flat config, ignores and file matching are configured insidepackage.json, not via CLI flags. Update to:eslint.config.jseslint --cache . - → remove the import/reference from
Cannot find module 'eslint-plugin-deprecation'(replaced byeslint.config.js)@typescript-eslint/no-deprecated - Other dead plugin imports → remove them from the config if the package was removed
C) No exists — auto-migration failed. Create one manually:
eslint.config.jsbash
ls node_modules/@grafana/eslint-config/flat.js 2>/dev/nullIf exists, create using it as the base:
flat.jseslint.config.jsjs
import grafanaConfig from '@grafana/eslint-config/flat';
export default [
...grafanaConfig,
{
ignores: ['**/dist/', '**/node_modules/', '**/.config/', '**/coverage/'],
},
];Then migrate any custom rules from the old into additional config objects in the array.
After creating the flat config:
.eslintrc- Update the script:
lint"lint": "eslint --cache ." - Delete the root (leave
.eslintrc— it's scaffolded and harmless).config/.eslintrc
bash
ls eslint.config.js .eslintrc* .config/.eslintrc* 2>/dev/null
npx eslint --version 2>&1三种场景:
A) 存在且执行成功 —— 自动迁移完成。继续执行后续步骤。
eslint.config.jsyarn lintB) 存在但执行失败 —— 迁移不完整。修复问题:
eslint.config.jsyarn lintbash
yarn lint 2>&1 | head -30常见修复方案:
- 或
Invalid option '--ignore-path'→ 从Invalid option '--ext'的package.json脚本中移除这些参数。在ESLint v9扁平配置中,忽略规则和文件匹配需在lint内配置,而非通过CLI参数。更新为:eslint.config.jseslint --cache . - → 从
Cannot find module 'eslint-plugin-deprecation'中移除相关导入/引用(已被eslint.config.js替代)@typescript-eslint/no-deprecated - 其他失效插件导入 → 如果对应包已被移除,从配置中删除相关内容
C) 不存在 —— 自动迁移失败。手动创建:
eslint.config.jsbash
ls node_modules/@grafana/eslint-config/flat.js 2>/dev/null如果存在,以此为基础创建:
flat.jseslint.config.jsjs
import grafanaConfig from '@grafana/eslint-config/flat';
export default [
...grafanaConfig,
{
ignores: ['**/dist/', '**/node_modules/', '**/.config/', '**/coverage/'],
},
];然后将旧中的自定义规则迁移到数组中的额外配置对象中。创建扁平配置后:
.eslintrc- 更新脚本:
lint"lint": "eslint --cache ." - 删除根目录下的(保留
.eslintrc——这是脚手架生成的,无影响).config/.eslintrc
Verify lint works
验证lint功能正常
bash
yarn lint 2>&1 | tail -20Fix auto-fixable issues with . Commit:
yarn lint --fixbash
git add -A && git diff --cached --quiet || git commit -m "chore: complete ESLint 9 flat config migration" --no-verifybash
yarn lint 2>&1 | tail -20使用修复可自动修复的问题。提交变更:
yarn lint --fixbash
git add -A && git diff --cached --quiet || git commit -m "chore: complete ESLint 9 flat config migration" --no-verifyStep 4: Externalize jsx-runtime
步骤4:外部化jsx-runtime
Always use the command. Requires a clean git working tree.
create-plugin addbash
npx @grafana/create-plugin@latest add externalize-jsx-runtime 2>&1Verify:
bash
grep "jsx-runtime" .config/bundler/externals.ts 2>/dev/null- Found → commit and proceed.
- Not found → command failed. Only then add externals manually to the root :
webpack.config.ts
ts
externals: ['react/jsx-runtime', 'react/jsx-dev-runtime'],Commit:
bash
git add -A && git diff --cached --quiet || git commit -m "feat: externalize jsx-runtime" --no-verify务必使用命令。需要干净的git工作区。
create-plugin addbash
npx @grafana/create-plugin@latest add externalize-jsx-runtime 2>&1验证:
bash
grep "jsx-runtime" .config/bundler/externals.ts 2>/dev/null- 找到内容 → 提交并继续。
- 未找到 → 命令执行失败。仅在此情况下手动将外部依赖添加到根目录的中:
webpack.config.ts
ts
externals: ['react/jsx-runtime', 'react/jsx-dev-runtime'],提交变更:
bash
git add -A && git diff --cached --quiet || git commit -m "feat: externalize jsx-runtime" --no-verifyStep 5: Bump grafanaDependency
grafanaDependency步骤5:提升grafanaDependency
版本
grafanaDependencybash
jq -r '.dependencies.grafanaDependency' $PLUGIN_JSONIf not already , update it. The in Step 3 may have already done this.
>=12.3.0create-plugin addbash
jq -r '.dependencies.grafanaDependency' $PLUGIN_JSON如果当前版本未达到,则进行更新。步骤3中的可能已完成此操作。
>=12.3.0create-plugin addStep 6: Bump dependencies
步骤6:提升依赖版本
Faro (if present)
Faro(如果存在)
bash
grep '"@grafana/faro' $PKG_JSON| Package | Target |
|---|---|
| |
| |
| |
bash
grep '"@grafana/faro' $PKG_JSON| 包名 | 目标版本 |
|---|---|
| |
| |
| |
Grafana packages
Grafana相关包
bash
grep '"@grafana/' $PKG_JSON | grep -v faro | grep -v create-pluginBump , , , to or later.
Add if the plugin uses translations or requires it.
@grafana/data@grafana/runtime@grafana/schema@grafana/ui^12.2.0@grafana/i18n@^12.2.0@grafana/scenesbash
grep '"@grafana/' $PKG_JSON | grep -v faro | grep -v create-plugin将、、、提升到或更高版本。如果插件使用翻译功能或需要,添加。
@grafana/data@grafana/runtime@grafana/schema@grafana/ui^12.2.0@grafana/scenes@grafana/i18n@^12.2.0React types
React类型定义
Bump and to (surfaces React 19 issues early).
Add and to devDependencies if missing.
reactreact-dom^18.3.0@types/react@^18.3.0@types/react-dom@^18.3.0将和提升到(可提前发现React 19相关问题)。如果缺失,在devDependencies中添加和。
reactreact-dom^18.3.0@types/react@^18.3.0@types/react-dom@^18.3.0Remove deprecated packages
移除废弃包
Remove from devDependencies if present:
- (replaced by
eslint-plugin-deprecation)@typescript-eslint/no-deprecated - (replaced by
@types/testing-library__jest-dom)setupTests.d.ts
如果devDependencies中存在以下包,将其移除:
- (已被
eslint-plugin-deprecation替代)@typescript-eslint/no-deprecated - (已被
@types/testing-library__jest-dom替代)setupTests.d.ts
Broken transitive dependencies
损坏的传递依赖
If fails with a stale git reference, do not edit yarn.lock. Add a entry:
yarn installresolutionsjson
"resolutions": {
"<package-name>": "<working-version-or-git-ref>"
}Then delete and and reinstall:
yarn.locknode_modulesbash
rm -rf node_modules yarn.lock
yarn install --ignore-engines 2>&1 | tail -10如果因陈旧的git引用失败,请勿编辑yarn.lock。添加配置项:
yarn installresolutionsjson
"resolutions": {
"<package-name>": "<working-version-or-git-ref>"
}然后删除和并重新安装:
yarn.locknode_modulesbash
rm -rf node_modules yarn.lock
yarn install --ignore-engines 2>&1 | tail -10Step 7: Fix unmet @openfeature/web-sdk
peer dependency
@openfeature/web-sdk步骤7:修复未满足的@openfeature/web-sdk
peer依赖
@openfeature/web-sdk@grafana/runtime@openfeature/react-sdk@openfeature/web-sdkCheck if the plugin uses yarn classic:
bash
yarn --version 2>&1 | head -1If version starts with , check for warnings:
1.bash
yarn install --ignore-engines 2>&1 | grep "unmet peer dependency.*openfeature/web-sdk"If warnings are found:
bash
yarn add -D @openfeature/web-sdk @openfeature/core --ignore-enginesSkip condition: Yarn v2+ or npm v7+ (peer deps are auto-installed).
@grafana/runtime@openfeature/react-sdk@openfeature/web-sdk检查插件是否使用Yarn经典版:
bash
yarn --version 2>&1 | head -1如果版本以开头,检查警告信息:
1.bash
yarn install --ignore-engines 2>&1 | grep "unmet peer dependency.*openfeature/web-sdk"如果发现警告:
bash
yarn add -D @openfeature/web-sdk @openfeature/core --ignore-engines跳过条件:使用Yarn v2+或npm v7+(会自动安装peer依赖)。
Step 8: Fix source code issues
步骤8:修复源码问题
bash
grep -rn "ReactDOM\.render\|ReactDOM\.unmountComponentAtNode\|ReactDOM\.findDOMNode" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.defaultProps\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.propTypes\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "contextTypes\|getChildContext" src/ --include="*.tsx" --include="*.ts"
grep -rn "createFactory" src/ --include="*.tsx" --include="*.ts"
grep -rn "ChangeEvent<HTMLInputElement>" src/ --include="*.tsx" --include="*.ts"| Pattern | Fix |
|---|---|
| |
| Move to destructured parameter defaults |
| Leave — still works |
| Remove |
| Use |
| Use JSX or |
| Change to |
bash
grep -rn "ReactDOM\.render\|ReactDOM\.unmountComponentAtNode\|ReactDOM\.findDOMNode" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.defaultProps\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.propTypes\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "contextTypes\|getChildContext" src/ --include="*.tsx" --include="*.ts"
grep -rn "createFactory" src/ --include="*.tsx" --include="*.ts"
grep -rn "ChangeEvent<HTMLInputElement>" src/ --include="*.tsx" --include="*.ts"| 匹配模式 | 修复方案 |
|---|---|
| 替换为 |
函数组件上的 | 迁移到解构参数默认值中 |
类组件上的 | 保留——仍可正常使用 |
| 删除 |
| 使用 |
| 使用JSX或 |
复选框上的 | 改为 |
Step 9: Build, typecheck, test
步骤9:构建、类型检查、测试
bash
rm -rf node_modules dist
yarn install --ignore-engines 2>&1 | tail -10
yarn build 2>&1 | tail -10
yarn typecheck 2>&1 | tail -10
yarn test --watchAll=false 2>&1 | tail -10| Error | Fix |
|---|---|
| Step 4 not applied — re-run |
| Step 7 — |
| |
| Bump |
| Add |
Stale git hash in | Add |
For detailed known issues (i18n crash, type breaks, publicPath mismatch), see
references/known-issues.md.
@grafana/schemabash
rm -rf node_modules dist
yarn install --ignore-engines 2>&1 | tail -10
yarn build 2>&1 | tail -10
yarn typecheck 2>&1 | tail -10
yarn test --watchAll=false 2>&1 | tail -10| 错误信息 | 修复方案 |
|---|---|
| 未执行步骤4——重新运行 |
| 执行步骤7—— |
| 执行 |
| 提升 |
仅含图标的 | 添加 |
| 在 |
关于详细的已知问题(i18n崩溃、类型错误、publicPath不匹配),请查看references/known-issues.md。
@grafana/schemaStep 10: Update CI (if applicable)
步骤10:更新CI(如适用)
bash
grep -rn "plugin-ci-workflows\|e2e-version" .github/workflows/ 2>/dev/null- or >= 6.0.0 → already tests React 19. No changes needed.
plugin-ci-workflows@main - → add
plugin-actions/e2e-version.skip-grafana-react-19-preview-image: false - Neither found → test manually with .
GRAFANA_VERSION=dev-preview-react19 docker compose up --build
bash
grep -rn "plugin-ci-workflows\|e2e-version" .github/workflows/ 2>/dev/null- 或版本>=6.0.0 → 已包含React 19测试。无需修改。
plugin-ci-workflows@main - → 添加
plugin-actions/e2e-version。skip-grafana-react-19-preview-image: false - 均未找到 → 使用手动测试。
GRAFANA_VERSION=dev-preview-react19 docker compose up --build
Step 11: Squash and push
步骤11:合并提交并推送
bash
git reset --soft origin/main
git add -A
git commit -m "fix: Prepare plugin for React 19 compatibility"Commit message body should list: create-plugin version change, ESLint 9 migration, key dependency
bumps, and any source code fixes.
bash
git reset --soft origin/main
git add -A
git commit -m "fix: Prepare plugin for React 19 compatibility"提交信息正文应列出:create-plugin版本变更、ESLint 9迁移、关键依赖版本提升以及所有源码修复内容。
References
参考资料
- Migration guide
- React 19 blog post for plugin developers
- React 19 changelog
- grafana-collector-app #1337 — full migration with create-plugin update + source fixes
- grafana/scenes issues — upstream i18n tracking
- 迁移指南
- 面向插件开发者的React 19博客文章
- React 19更新日志
- grafana-collector-app #1337 —— 包含create-plugin更新和源码修复的完整迁移示例
- grafana/scenes问题 —— 上游i18n问题追踪