Build a panel plugin

Introduction

Panels are the building blocks of Grafana. They allow you to visualize data in different ways. While Grafana has several types of panels already built-in, you can also build your own panel, to add support for other visualizations.

For more information about panels, refer to the documentation on Panels.

Prerequisites

  • Grafana 7.0
  • NodeJS 12.x
  • yarn

Set up your environment

Before you can get started building plugins, you need to set up your environment for plugin development.

To discover plugins, Grafana scans a plugin directory, the location of which depends on your operating system.

  1. Create a directory called grafana-plugins in your preferred workspace.

  2. Find the plugins property in the Grafana configuration file and set the plugins property to the path of your grafana-plugins directory. Refer to the Grafana configuration documentation for more information.

    [paths]
    plugins = "/path/to/grafana-plugins"
    
  3. Restart Grafana if it’s already running, to load the new configuration.

Alternative method: Docker

If you don’t want to install Grafana on your local machine, you can use Docker.

To set up Grafana for plugin development using Docker, run the following command:

docker run -d -p 3000:3000 -v "$(pwd)"/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana:7.0.0

Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.

docker restart grafana

Create a new plugin

Tooling for modern web development can be tricky to wrap your head around. While you certainly can write your own webpack configuration, for this guide, you’ll be using grafana-toolkit.

grafana-toolkit is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The toolkit takes care of building and testing for you.

  1. In the plugin directory, create a plugin from template using the plugin:create command:

    npx @grafana/toolkit plugin:create my-plugin
    
  2. Change directory.

    cd my-plugin
    
  3. Download necessary dependencies:

    yarn install
    
  4. Build the plugin:

    yarn dev
    
  5. Restart the Grafana server for Grafana to discover your plugin.

  6. Open Grafana and go to Configuration -> Plugins. Make sure that your plugin is there.

By default, Grafana logs whenever it discovers a plugin:

INFO[01-01|12:00:00] Registering plugin       logger=plugins name=my-plugin

Anatomy of a plugin

Plugins come in different shapes and sizes. Before we dive deeper, let’s look at some of the properties that are shared by all of them.

Every plugin you create will require at least two files: plugin.json and module.ts.

plugin.json

When Grafana starts, it scans the plugin directory for any subdirectory that contains a plugin.json file. The plugin.json file contains information about your plugin, and tells Grafana about what capabilities and dependencies your plugin needs.

While certain plugin types can have specific configuration options, let’s look at the mandatory ones:

  • type tells Grafana what type of plugin to expect. Grafana supports three types of plugins: panel, datasource, and app.
  • name is what users will see in the list of plugins. If you’re creating a data source, this is typically the name of the database it connects to, such as Prometheus, PostgreSQL, or Stackdriver.
  • id uniquely identifies your plugin, and should start with your Grafana username, to avoid clashing with other plugins. Sign up for a Grafana account to claim your username.

To see all the available configuration settings for the plugin.json, refer to the plugin.json Schema.

module.ts

After discovering your plugin, Grafana loads the module.ts file, the entrypoint for your plugin. module.ts exposes the implementation of your plugin, which depends on the type of plugin you’re building.

Specifically, module.ts needs to expose an object that extends GrafanaPlugin, and can be any of the following:

Panel plugins

Since Grafana 6.x, panels are ReactJS components.

Prior to Grafana 6.0, plugins were written in AngularJS. Even though we still support plugins written in AngularJS, we highly recommend that you write new plugins using ReactJS.

Panel properties

The PanelProps interface exposes runtime information about the panel, such as panel dimensions, and the current time range.

You can access the panel properties through props, as seen in your plugin.

src/SimplePanel.tsx

const { options, data, width, height } = props;

Development workflow

Next, you’ll learn the basic workflow of making a change to your panel, building it, and reloading Grafana to reflect the changes you made.

First, you need to add your panel to a dashboard:

  1. Open Grafana in your browser.
  2. Create a new dashboard, and add a new panel.
  3. Select your panel from the list of visualization types.
  4. Save the dashboard.

Now that you can view your panel, try making a change to the panel plugin:

  1. In SimplePanel.tsx, change the fill color of the circle.
  2. Run yarn dev to build the plugin.
  3. In the browser, reload Grafana with the new changes.

Add panel options

Sometimes you want to offer the users of your panel an option to configure the behavior of your plugin. By configuring panel options for your plugin, your panel will be able to accept user input.

In the previous step, you changed the fill color of the circle in the code. Let’s change the code so that the plugin user can configure the color from the panel editor.

Add an option

Panel options are defined in a panel options object. SimpleOptions is an interface that describes the options object.

  1. In types.ts, add a CircleColor type to hold the colors the users can choose from:

    type CircleColor = 'red' | 'green' | 'blue';
    
  2. In the SimpleOptions interface, add a new option called color:

    color: CircleColor;
    

Here’s the updated options definition:

src/types.ts

type SeriesSize = 'sm' | 'md' | 'lg';
type CircleColor = 'red' | 'green' | 'blue';

// interface defining panel options type
export interface SimpleOptions {
  text: string;
  showSeriesCount: boolean;
  seriesCountSize: SeriesSize;
  color: CircleColor;
}

Add an option control

To change the option from the panel editor, you need to bind the color option to an option control.

Grafana supports a range of option controls, such as text inputs, switches, and radio groups.

Let’s create a radio control and bind it to the color option.

  1. In src/module.ts, add the control at the end of the builder:

    .addRadio({
      path: 'color',
      name: 'Circle color',
      defaultValue: 'red',
      settings: {
        options: [
          {
            value: 'red',
            label: 'Red',
          },
          {
            value: 'green',
            label: 'Green',
          },
          {
            value: 'blue',
            label: 'Blue',
          },
        ],
      }
    });
    

    The path is used to bind the control to an option. You can bind a control to nested option by specifying the full path within a options object, for example colors.background.

Grafana builds an options editor for you and displays it in the panel editor sidebar in the Display section.

Use the new option

You’re almost done. You’ve added a new option and a corresponding control to change the value. But the plugin isn’t using the option yet. Let’s change that.

  1. To convert option value to the colors used by the current theme, add a switch statement right before the return statement in SimplePanel.tsx.

    src/SimplePanel.tsx

    let color: string;
    switch (options.color) {
      case 'red':
        color = theme.palette.redBase;
        break;
      case 'green':
        color = theme.palette.greenBase;
        break;
      case 'blue':
        color = theme.palette.blue95;
        break;
    }
    
  2. Configure the circle to use the color.

    <g>
      <circle style={{ fill: color }} r={100} />
    </g>
    

Now, when you change the color in the panel editor, the fill color of the circle changes as well.

Create dynamic panels using data frames

Most panels visualize dynamic data from a Grafana data source. In this step, you’ll create one circle per series, each with a radius equal to the last value in the series.

To use data from queries in your panel, you need to set up a data source. If you don’t have one available, you can use the TestData DB data source while developing.

The results from a data source query within your panel are available in the data property inside your panel component.

const { data } = props;

data.series contains the series returned from a data source query. Each series is represented as a data structure called data frame. A data frame resembles a table, where data is stored by columns, or fields, instead of rows. Every value in a field share the same data type, such as string, number, or time.

Here’s an example of a data frame with a time field, Time, and a number field, Value:

Time Value
1589189388597 32.4
1589189406480 27.2
1589189513721 15.0

Let’s see how you can retrieve data from a data frame and use it in your visualization.

  1. Get the last value of each field of type number, by adding the following to SimplePanel.tsx, before the return statement:

    const radii = data.series
     .map(series => series.fields.find(field => field.type === 'number'))
     .map(field => field?.values.get(field.values.length - 1));
    

    radii will contain the last values in each of the series that are returned from a data source query. You’ll use these to set the radius for each circle.

  2. Change the svg element to the following:

    <svg
      className={styles.svg}
      width={width}
      height={height}
      xmlns="http://www.w3.org/2000/svg"
      xmlnsXlink="http://www.w3.org/1999/xlink"
      viewBox={`0 -${height / 2} ${width} ${height}`}
    >
      <g fill={color}>
        {radii.map((radius, index) => {
          const step = width / radii.length;
          return <circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} />;
        })}
      </g>
    </svg>
    

    Note how we’re creating a <circle> element for each value in radii:

    {radii.map((radius, index) => {
      const step = width / radii.length;
      return <circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} />;
    })}
    

    We use the transform here to distribute the circle horizontally within the panel.

  3. Rebuild your plugin and try it out by adding multiple queries to the panel. Refresh the dashboard.

If you want to know more about data frames, check out our introduction to Data frames.

Publish your plugin

Once you’re happy with your plugin, it’s time to package it, and submit to the plugin repository.

For users to be able to use the plugin without building it themselves, you need to make a production build of the plugin, and commit to a release branch in your repository.

To submit a plugin to the plugin repository, you need to create a release of your plugin. While we recommend following the branching strategy outlined below, you’re free to use one that makes more sense to you.

Create a plugin release

Let’s create version 0.1.0 of our plugin.

  1. Create a branch called release-0.1.x.

    git checkout -b release-0.1.x
    
  2. Do a production build.

    yarn build
    
  3. Add the dist directory.

    git add -f dist
    
  4. Create the release commit.

    git commit -m "Release v0.1.0"
    
  5. Create a release tag.

    git tag -a v0.1.0 -m "Create release tag v0.1.0"
    
  6. Push to GitHub. follow-tags tells Git to push the release tag along with our release branch.

    git push --set-upstream origin release-0.1.x --follow-tags
    

Submit the plugin

For a plugin to be published on Grafana Plugins, it needs to be added to the grafana-plugin-repository.

  1. Fork the grafana-plugin-repository

  2. Add your plugin to the repo.json file in the project root directory:

    {
      "id": "<plugin id>",
      "type": "<plugin type>",
      "url": "https://github.com/<username>/my-plugin",
      "versions": [
        {
          "version": "<version>",
          "commit": "<git sha>",
          "url": "https://github.com/<username>/my-plugin"
        }
      ]
    }
    
  3. Create a pull request.

Once your plugin has been accepted, it’ll be published on Grafana Plugin, available for anyone to install!

We’re auditing every plugin that’s added to make sure it’s ready to be published. This means that it might take some time before your plugin is accepted. We’re working on adding more automated tests to improve this process.

Congratulations

Congratulations, you made it to the end of this tutorial!