admission-control
Use when the user asks to "write a validator", "add validation", "implement admission…
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.
$ npx -y skills add grafana/skills --skill react-19-plugin-migration --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/react-19-plugin-migrationContext preview
The summary Claude sees to decide when to auto-load this skill.
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.
name: react-19-plugin-migration license: Apache-2.0 description: Migrate a Grafana plugin to React 19 compatibility. Use when the user asks to update a plugin for React 19, prepare for React 19, fix React 19 compatibility, upgrade to React 19, migrate to React 19, bump grafanaDependency to 12.3.0, externalize jsx-runtime, or run react-detect. Triggers on phrases like "update plugin for React 19", "React 19 migration", "prepare for React 19", "plugin React 19 compat", "grafanaDependency 12.3.0", "JSX runtime externals", "react-detect", "SECRET_INTERNALS", "ReactCurrentOwner", or "ReactCurrentDispatcher".
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.lock`.
---
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 `PLUGIN_ID` is empty, ask the user for the plugin root path.
---
Build the plugin and run the React 19 compatibility scanner:
npm run build 2>&1 | tail -5 npx -y @grafana/react-detect@latest 2>&1
Save the output. It flags:
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.
---
The scaffolding update brings in externals extraction, jest mocks, Docker fixes, and webpack improvements needed for React 19. **Always do this before `add externalize-jsx-runtime`.**
Requires a clean git working tree. Create a feature branch first if not already on one.
npx @grafana/create-plugin@latest update 2>&1
The update runs an intermediate `yarn install` without `--ignore-engines`. Complete it manually:
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10
Commit the intermediate state and re-run:
git add -A && git commit -m "chore: intermediate create-plugin update" --no-verify npx @grafana/create-plugin@latest update 2>&1
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:
git add -A && git commit -m "chore: update create-plugin (ESLint 9 migration manual)" --no-verify
Then follow the "Complete ESLint 9 migration" section below to finish.
Always run install and verify:
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10 cat .config/.cprc.json
Commit if there are changes:
git add -A && git diff --cached --quiet || git commit -m "chore: update create-plugin scaffolding" --no-verify
---
The `create-plugin update` bumps ESLint to v9, which requires flat config (`eslint.config.js`) instead of `.eslintrc`. Whether the auto-migration (004) succeeded, partially succeeded, or failed, **you must ensure ESLint works before proceeding**.
ls eslint.config.js .eslintrc* .config/.eslintrc* 2>/dev/null npx eslint --version 2>&1
Three scenarios:
**A) `eslint.config.js` exists and `yarn lint` passes** — auto-migration succeeded. Proceed.
**B) `eslint.config.js` exists but `yarn lint` fails** — partial migration. Fix the issues:
yarn lint 2>&1 | head -30
Common fixes:
the `lint` script in `package.json`. In ESLint v9 flat config, ignores and file matching are configured inside `eslint.config.js`, not via CLI flags. Update to: `eslint --cache .`
`eslint.config.js` (replaced by `@typescript-eslint/no-deprecated`)
**C) No `eslint.config.js` exists** — auto-migration failed. Create one manually:
ls node_modules/@grafana/eslint-config/flat.js 2>/dev/null
If `flat.js` exists, create `eslint.config.js` using it as the base:
import grafanaConfig from '@grafana/eslint-config/flat';
export default [
...grafanaConfig,
{
ignores: ['**/dist/', '**/node_modules/', '**/.config/', '**/coverage/'],
},
];Then migrate any custom rules from the old `.eslintrc` into additional config objects in the array. After creating the flat config:
1. Update the `lint` script: `"lint": "eslint --cache ."` 2. Delete the root `.eslintrc` (leave `.config/.eslintrc` — it's scaffolded and harmless)
yarn lint 2>&1 | tail -20
Fix auto-fixable issues with `yarn lint --fix`. Commit:
git add -A && git diff --cached --quiet || git commit -m "chore: complete ESLint 9 flat config migration" --no-verify
Public skills for working with Grafana, Prometheus, Loki, Tempo, Pyroscope, k6, and the broader LGTM observability stack. Compatible with Claude Code, Cursor, Codex, and any tool supporting the Agent Skills open standard.
Repo: grafana/skills
Use when the user asks to "write a validator", "add validation", "implement admission…
Use when starting any grafana-app-sdk work — scaffolding a Grafana app, initializing a…
Author CUE kind definitions for grafana-app-sdk apps - schemas, versioning, field…
Implement reconcilers and watchers for grafana-app-sdk apps — write…
Cut Grafana Cloud Metrics cost by shrinking active-series count with Adaptive Metrics…