Skip to main content

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.

warning

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.

info

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 propTypes checks and defaultProps on function components
  • Removal of legacy context API (contextTypes and getChildContext)
  • Removal of string refs
  • Removal of createFactory
  • Removal of ReactDOM.findDOMNode
  • Removal of ReactDOM.render and ReactDOM.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:

warning

Applying this fix makes your plugin incompatible with versions of Grafana earlier than 12.3.0.

  1. Create a webpack.config.ts file 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;
  1. Update the plugin's package.json to use the new webpack configuration:
"scripts": {
"build": "webpack -c ./webpack.config.ts --env production",
"dev": "webpack -w -c ./webpack.config.ts --env development"
}
  1. Change the grafanaDependency in src/plugin.json to >=12.3.0 to 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.

note

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.

note

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.

note

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.