Migrate plugins from Grafana version 12.x to 13.x
This guide helps you migrate plugins from Grafana version 12.x to 13.x.
Prerequisites
Before starting the migration:
- Back up your plugin code
- Ensure your development environment is up to date
- Build your plugin with
npm run build
React 19 upgrade
As part of the Grafana 13 release, Grafana has updated from React 18 to React 19. This update ensures Grafana stays aligned with the broader React ecosystem and benefits from ongoing performance enhancements and new functionality.
For most plugins, this update requires only minor code changes and a dependency audit to ensure compatibility with React 19.
How this update impacts plugins
Grafana shares a single React instance with all loaded plugins at runtime. This means updating the React version in your plugin's package.json file doesn't change the runtime version. Instead, the goal is to align to Grafana's runtime and focus on forward-compatible code.
Don't attempt to force a different React version or bundle React. Pinning a different version locally results in a test environment that is inconsistent with the Grafana runtime environment.
Detect compatibility issues
Use the @grafana/react-detect CLI tool to scan your plugin's built JavaScript files and source code for potential compatibility issues. Run the following commands from the root of your plugin:
npm run build
npx -y @grafana/react-detect@latest
The output identifies locations in your source code or dependencies that use React features affected by the breaking changes in React 19.
The CLI tool can create false positives, particularly if your source code or a dependency supports multiple versions of React. It's meant to be a first step in identifying where incompatible code may live.
As you address issues in your plugin, re-run npm run build before running the react-detect CLI again.
React 19 breaking changes
React 19 introduces the following breaking changes that may affect the functionality of plugins:
- Removal of
propTypeschecks anddefaultPropson function components - Removal of legacy context API (
contextTypesandgetChildContext) - Removal of string refs
- Removal of
createFactory - Removal of
ReactDOM.findDOMNode - Removal of
ReactDOM.renderandReactDOM.unmountComponentAtNode - Renaming of internal React API
__SECRET_INTERNALS_DO_NOT_USE
Refer to the React 19 upgrade guide for a full list of breaking changes.
Fix the jsx-runtime issue
The __SECRET_INTERNALS_DO_NOT_USE API rename is the most likely cause of plugin loading issues. Some React dependencies and the react/jsx-runtime module rely on these internals. In React 19, these internals were renamed, which means dependencies that expect the old name may fail or crash at runtime.
To solve this issue, externalize the jsx-runtime modules in your plugin's webpack configuration:
Applying this fix makes your plugin incompatible with versions of Grafana earlier than 12.3.0.
- Create a
webpack.config.tsfile in the root of your plugin's repository:
import type { Configuration } from 'webpack';
import { merge } from 'webpack-merge';
import grafanaConfig, { type Env } from './.config/webpack/webpack.config';
const config = async (env: Env): Promise<Configuration> => {
const baseConfig = await grafanaConfig(env);
return merge(baseConfig, {
externals: ['react/jsx-runtime', 'react/jsx-dev-runtime'],
});
};
export default config;
- Update the plugin's
package.jsonto use the new webpack configuration:
"scripts": {
"build": "webpack -c ./webpack.config.ts --env production",
"dev": "webpack -w -c ./webpack.config.ts --env development"
}
- Change the
grafanaDependencyinsrc/plugin.jsonto>=12.3.0to signal to plugin users that the plugin no longer supports older versions of Grafana.
Common dependency issues
The following sections list common plugin dependencies that @grafana/react-detect may flag and the recommended actions to resolve them.
Dependencies that require the jsx-runtime fix
These dependencies bundle react/jsx-runtime at build time. At runtime this causes the plugin to fail because React 19 has renamed its internal API. To solve this issue, follow the steps in Fix the jsx-runtime issue to externalize the jsx-runtime modules. Your plugin will need to be rebuilt and republished, and will only load in versions of Grafana >=12.3.0.
@tanstack/react-query
Flags: jsxRuntimeImport
This package uses the tsconfig setting "jsx": "react-jsx" which causes the plugin to bundle the React 18 version of react/jsx-runtime at build time. Apply the jsx-runtime fix or replace this dependency with one that offers the same functionality.
@grafana/faro-react
Flags: jsxRuntimeImport
Before v2.2.3, this package used the tsconfig setting "jsx": "react-jsx" which causes the plugin to bundle the React 18 version of react/jsx-runtime at build time. Either bump the version of @grafana/faro-react to ^2.2.3 (which resolves the issue without the jsx-runtime fix) or apply the jsx-runtime fix.
react-window
Flags: defaultProps, jsxRuntimeImport
Compatible with React 18 and 19 — make sure to use ^1.8.11. All usage of defaultProps are in class components, which isn't a breaking change. However, this package uses the tsconfig setting "jsx": "react-jsx" which causes the plugin to bundle the React 18 version of react/jsx-runtime at build time. Apply the jsx-runtime fix or replace this dependency.
react-markdown
Flags: propTypes, jsxRuntimeImport
propTypes were removed in v9 of react-markdown. They are considered a warning as they are silently ignored at runtime, and if you were relying on them, the props your plugin passes would already pass propType validation. This package imports react/jsx-runtime which causes the plugin to bundle the React 18 version of react/jsx-runtime at build time. Apply the jsx-runtime fix or replace this dependency.
Dependencies that require a version bump
These dependencies have released React 19-compatible versions. Update to the specified minimum version to resolve any flagged issues.
react-draggable
Flags: findDOMNode | Minimum version: ^4.4.0
Compatible with React 18 and 19. The library added a findDOMNode method to the Draggable class. Pass the nodeRef prop to avoid findDOMNode usage. See example.
react-resizable
Flags: defaultProps, propTypes, findDOMNode | Minimum version: ^3.1.3
Compatible with React 18 and 19. defaultProps is a false positive — they are used on a class component. findDOMNode is a false positive. propTypes are silently ignored at runtime. Uses react-draggable under the hood but passes nodeRef.
rc-resize-observer
Flags: findDOMNode | Minimum version: ^1.0.0
Compatible with React 18 and 19. Depends on the rc-util package which removed findDOMNode in v1.1.0.
This package appears to have been renamed to @rc-component/resize-observer. Consider updating.
rc-motion
Flags: findDOMNode | Minimum version: ^2.9.5
Compatible with React 18 and 19. Depends on the rc-util package which removed findDOMNode in v1.1.0.
This package appears to have been renamed to @rc-component/motion. Consider updating.
rc-trigger
Flags: findDOMNode, defaultProps | Minimum version: ^5.3.4
Compatible with React 18 and 19. Depends on the rc-util package which removed findDOMNode in v1.1.0. Trigger is a class component with defaultProps, which is not a breaking change.
This package appears to have been renamed to @rc-component/trigger. Consider updating.
@hello-pangea/dnd
Flags: ReactDOM.render | Minimum version: ^18.0.0
Compatible with React 18 and 19. The ReactDOM.render flag is a false positive — there is no reference to ReactDOM.render in the codebase.
@react-awesome-query-builder/ui
Flags: defaultProps, propTypes | Minimum version: ^6.6.15
Compatible with React 18 and 19. All usage of defaultProps are in class components, which isn't a breaking change. propTypes are silently ignored at runtime, and if you were relying on them, the props your plugin passes would already pass propType validation.
react-virtualized-auto-sizer
Flags: defaultProps | Minimum version: ^2.0.0
defaultProps were removed in v2.
Dependencies with warnings
These dependencies are compatible with React 19 but may have behavioral changes if not used correctly.
react-transition-group
Flags: defaultProps ⚠️, findDOMNode ⚠️, propTypes
defaultProps are considered a warning as the component should render but behavior may be compromised. Make sure to pass the nodeRef prop to the react-transition-group components, otherwise the library attempts to call ReactDOM.findDOMNode which results in errors. propTypes are silently ignored at runtime. See details.
False positives
The following dependencies are flagged by @grafana/react-detect but don't require any changes:
@grafana/scenes
Flags: ReactDOM.render
False positive caused by react-use/esm/useScratch.js which imports { render } from a different React package.
style-mod
Flags: ReactDOM.render
False positive caused by src/style-mod.js which has its own render function.
Dependency fix details
react-draggable: pass the nodeRef prop
To avoid findDOMNode usage, pass a nodeRef prop to the Draggable component:
function MyComponent() {
const nodeRef = React.useRef(null);
return (
<Draggable nodeRef={nodeRef}>
<div ref={nodeRef}>Example Target</div>
</Draggable>
);
}
react-transition-group: pass the nodeRef prop
Pass the nodeRef prop to react-transition-group components. Without it, the library attempts to call ReactDOM.findDOMNode, which results in errors in React 19.
Be aware of the following defaultProps on function components that are no longer applied in React 19. If you rely on default behavior, explicitly pass these props:
SwitchTransition:{ mode: 'out-in' }Transition:{ in: false, mountOnEnter: false, unmountOnExit: false, appear: false, enter: true, exit: true }TransitionGroup:{ component: 'div', childFactory: (child) => child }
Verify fixes locally
To test your plugin against React 19, use the developer preview Docker image:
GRAFANA_VERSION=dev-preview-react19 docker compose up --build
Once running, navigate your plugin's features to check that everything behaves as expected. If you have end-to-end (e2e) tests, run them against the dev-preview-react19 image to help identify any problems.
After verifying React 19 compatibility, also verify that the plugin continues to work in a build of Grafana that uses React 18 to ensure backward compatibility.
Update CI pipelines
To make sure your plugin stays compatible with multiple Grafana versions that include React 19, use the e2e testing workflow that automatically runs your tests against multiple Grafana versions.
If you're already using the e2e testing workflow (scaffolded by default), add the skip-grafana-react-19-preview-image input parameter:
- name: Resolve Grafana E2E versions
id: resolve-versions
uses: grafana/plugin-actions/e2e-version@e2e-version/v1.2.1
+ with:
+ skip-grafana-react-19-preview-image: false
Common questions
Should I already start using React 19 functionality?
Please do not upgrade React to 19 in any plugins. We are focusing on forward-compatible code right now because we need to easily switch back to a React 18 Grafana build if something goes wrong. We cannot do that if plugins start adopting new React 19 features. Once the dust has settled, we will update the @grafana/runtime, @grafana/data, and @grafana/ui npm packages to officially support React 19.