Skip to main content

Creating Custom Plugins

1. Setting Up Plugin Development (@grafana/create-plugin)

Example: Initialize a new Grafana plugin

npm init @grafana/plugin my-plugin
Command Description Notes
npm init @grafana/plugin <plugin-name> Creates a new Grafana plugin project Uses the official Grafana plugin template
npm run build Builds the plugin for production Generates optimized bundles in dist/
npm run dev Starts the development server Watches for file changes and rebuilds automatically
Note: The @grafana/create-plugin tool scaffolds a plugin with the latest Grafana plugin structure and dependencies.
Warning: Ensure that your Grafana instance is running on the same host or accessible via network for plugin development.

2. Creating Panel Plugins

Panel plugins in Grafana allow you to create custom visualizations for dashboards. They are built using the Grafana Plugin SDK and can be developed in TypeScript or JavaScript.

Property Description Default Value
id Unique identifier for the panel plugin None
name Name of the panel plugin None
type Must be set to "panel" None
version Version of the plugin 1.0.0

Example: Basic Panel Plugin Structure

import { PanelPlugin } from '@grafana/data';

export const plugin = new PanelPlugin(MyPanel)
  .setPanelOptions(builder => {
    return builder.addTextInput({
      path: 'myOption',
      name: 'My Option',
      description: 'Description of my option',
    });
  });

Example: Panel Component

import React from 'react';
import { PanelProps } from '@grafana/data';

interface MyPanelOptions {
  myOption: string;
}

export const MyPanel: React.FC<PanelProps<MyPanelOptions>> = ({ options, data }) => {
  return (
    <div>
      <p>My Option Value: {options.myOption}</p>
    </div>
  );
};
Note: Panel plugins must be built using the Grafana Plugin SDK and must follow the plugin structure defined by Grafana.
Warning: Ensure that the panel plugin is compatible with the Grafana version you are using.

3. Creating Data Source Plugins

Data source plugins in Grafana allow you to connect to external data sources and query them directly from Grafana dashboards. These plugins are essential for extending Grafana's capabilities to work with various data stores.

Property Description Values
id Unique identifier for the plugin my-datasource
name Display name of the plugin My Data Source
type Must be datasource datasource
backend Indicates if the plugin uses backend true

Example: Plugin Configuration

{
  "id": "my-datasource",
  "name": "My Data Source",
  "type": "datasource",
  "backend": true,
  "info": {
    "description": "A custom data source plugin",
    "version": "1.0.0"
  }
}
Note: The backend property must be set to true for plugins that require server-side processing.

3. Plugin Structure

A typical data source plugin structure includes the following components:

  • src: Source code directory
  • dist: Compiled plugin files
  • module.ts: Main module file
  • datasource.ts: Data source implementation
  • plugin.json: Plugin metadata

Example: Basic Plugin Structure

my-datasource/
├── src/
│   ├── module.ts
│   └── datasource.ts
├── dist/
├── plugin.json

3. Data Source Implementation

The data source implementation handles queries to the external system and returns results in a format Grafana can understand.

Method Description Return Type
query Handles query execution Promise<DataFrame[]>
testDatasource Tests connection to the data source Promise<any>
annotationQuery Handles annotation queries Promise<any>

Example: Query Method

class MyDataSource extends DataSourcePlugin<MyQuery, MyDataSourceOptions> {
  async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const { targets } = options;
    return targets.map(target => ({
      data: [
        {
          fields: [
            { name: 'time', values: [1, 2, 3] },
            { name: 'value', values: [10, 20, 30] }
          ]
        }
      ]
    }));
  }
}

3. Configuration Options

Data source plugins can support various configuration options to allow users to customize their connection.

Option Type Description Required
url string Endpoint URL for the data source true
authToken string Authentication token for the data source false
timeout number Request timeout in milliseconds false

Example: Configuration in Plugin JSON

{
  "name": "My Data Source",
  "id": "my-datasource",
  "type": "datasource",
  "jsonData": {
    "url": "https://api.example.com",
    "timeout": 5000
  }
}

4. Creating App Plugins

App plugins in Grafana are extensions that provide a complete application experience within the Grafana interface. They can include dashboards, data sources, panels, and more.

Property Description Value
plugin.json Configuration file for the plugin { "id": "my-app-plugin", "name": "My App Plugin", "type": "app", "version": "1.0.0" }
appPlugin Entry point for the app plugin src/app.ts
rootPath Root path of the plugin in the Grafana UI /a/my-app-plugin

Example: Plugin Configuration

{
  "id": "my-app-plugin",
  "name": "My App Plugin",
  "type": "app",
  "version": "1.0.0",
  "appPlugin": {
    "rootPath": "/a/my-app-plugin"
  }
}

Example: App Plugin Entry Point

import { AppPlugin } from '@grafana/data';
import { App } from './components/App';

export const plugin = new AppPlugin<AppSettings>()
  .setRootComponent(App);
Note: App plugins must be built and packaged before they can be installed in Grafana.
Warning: App plugins are not supported in Grafana Cloud.

Key Features of App Plugins

  • Can include custom dashboards and data sources
  • Supports navigation within the Grafana UI
  • Can define custom routes and pages
  • Can integrate with Grafana's plugin system

5. Implementing Plugin Options

Option Type Description Default
pluginId string Unique identifier for the plugin null
name string Display name of the plugin "Custom Plugin"
description string Short description of the plugin's purpose "A custom Grafana plugin"
version string Plugin version (semantic versioning) "1.0.0"
backend boolean Indicates if the plugin has a backend component false

Example: Plugin Configuration

{
  "pluginId": "my-custom-plugin",
  "name": "My Custom Plugin",
  "description": "A plugin that visualizes custom data sources",
  "version": "2.1.0",
  "backend": true
}
Note: The pluginId must be unique across all Grafana plugins and follow the format org-pluginName.
Warning: Changing the pluginId after deployment will break existing dashboards using this plugin.

6. Using Grafana UI Library

The Grafana UI Library provides a set of React components and utilities to help developers build custom plugins with a consistent look and feel that matches Grafana's design language.

Component Description Usage
Button A standard button component with multiple variants (primary, secondary, etc.) <Button variant="primary">Click Me</Button>
Card A container component for grouping related content <Card><Card.Title>Title</Card.Title></Card>
Input Form input component with validation and styling <Input placeholder="Enter value" />
Select A dropdown selector component <Select options={options} onChange={handleChange} />
Switch A toggle switch for binary options <Switch checked={value} onChange={handleChange} />

Example: Using Button Component

import { Button } from "@grafana/ui";

function MyComponent() {
  return (
    <Button variant="primary" onClick={() => console.log("Clicked!")}>
      Primary Button
    </Button>
  );
}

Example: Using Card Component

import { Card, CardBody, CardTitle } from "@grafana/ui";

function MyCard() {
  return (
    <Card>
      <CardTitle>Dashboard Card</CardTitle>
      <CardBody>
        <p>This is a sample card content.</p>
      </CardBody>
    </Card>
  );
}
Note: Ensure that you have installed the @grafana/ui package in your plugin project to use these components.
Warning: The Grafana UI Library is still in development, and some components may change or be deprecated in future versions.

7. Using Plugin Data API

Method Description Parameters Return Type
query Fetches data from a data source options: QueryOptions Promise<DataFrame[]>
annotationQuery Fetches annotation data for a dashboard options: AnnotationQueryOptions Promise<AnnotationEvent[]>
metricFindQuery Performs a metric find query to return a list of strings or objects query: string Promise<MetricFindValue[]>

Example: Querying Data

import { DataQueryRequest, DataQueryResponse } from '@grafana/data';

export class MyDataSource extends DataSourceApi<MyQuery> {
  async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const { targets } = options;
    const results = await Promise.all(
      targets.map(async (target) => {
        const response = await this.backendSrv.get(`/api/plugins/my-plugin/resources/data`, {
          params: { query: target.query },
        });
        return {
          refId: target.refId,
          data: response.data,
        };
      })
    );
    return { data: results };
  }
}

Example: Annotation Query

import { AnnotationQueryRequest } from '@grafana/data';

export class MyAnnotationPlugin extends DataSourceApi<MyAnnotationQuery> {
  async annotationQuery(options: AnnotationQueryRequest<MyAnnotationQuery>): Promise<AnnotationEvent[]> {
    const response = await this.backendSrv.get(`/api/plugins/my-plugin/resources/annotations`, {
      params: { timeFrom: options.range.from.unix(), timeTo: options.range.to.unix() },
    });
    return response.data.map((event: any) => ({
      ...event,
      time: event.time.unix(),
    }));
  }
}

Example: Metric Find Query

export class MyMetricFindQueryPlugin extends DataSourceApi<MyQuery> {
  async metricFindQuery(query: string): Promise<MetricFindValue[]> {
    const response = await this.backendSrv.get(`/api/plugins/my-plugin/resources/metrics`, {
      params: { query },
    });
    return response.data.map((item: any) => ({
      text: item.name,
      value: item.id,
    }));
  }
}
Note: The Plugin Data API is used to fetch data from backend services or external APIs. It supports various query types including standard queries, annotation queries, and metric find queries.

8. Testing Plugins Locally

Step Description Command
1 Start the Grafana development server npm run dev
2 Build the plugin for development npm run build
3 Watch for code changes and rebuild automatically npm run watch

Example: Plugin Development Setup

mkdir my-grafana-plugin
cd my-grafana-plugin
npm init -y
npm install @grafana/cli @grafana/data @grafana/runtime
Note: Ensure Grafana is running locally and the plugin is installed in the plugins directory.
Warning: Avoid using npm install inside the plugin directory as it may cause dependency conflicts.

Testing Workflow Summary

  • Use npm run dev to start Grafana in development mode.
  • Use npm run build to compile the plugin code.
  • Use npm run watch for automatic rebuilding on file changes.

9. Building Plugins

Example: Basic Plugin Structure

// plugin.json
{
  "id": "myorg-myplugin",
  "name": "My Plugin",
  "type": "panel",
  "version": "1.0.0",
  "backend": false,
  "dependencies": {
    "grafanaVersion": "^10.0.0"
  }
}
Field Description Required
id Unique identifier for the plugin Yes
name Display name of the plugin Yes
type Plugin type (panel, datasource, app) Yes
version Plugin version (semantic versioning) Yes
backend Whether the plugin uses a backend server No
dependencies.grafanaVersion Minimum Grafana version supported Yes
Note: Plugin IDs must be unique across all Grafana plugins.

Example: Panel Plugin Component

// src/panel.ts
import { PanelPlugin } from "@grafana/data";

export const plugin = new PanelPlugin(MyPanel)
  .setPanelOptions(builder => {
    return builder.addTextInput({
      path: "showTitle",
      name: "Show Title",
      description: "Toggle title display",
      defaultValue: true
    });
  });

Example: Datasource Plugin

// src/datasource.ts
import { DataSourceApi } from "@grafana/data";

export class MyDataSource extends DataSourceApi<MyQuery> {
  constructor(instanceSettings: DataSourceInstanceSettings) {
    super(instanceSettings);
  }

  async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    // Query logic here
  }
}

Plugin Development Workflow

  1. Create plugin directory structure
  2. Define plugin.json
  3. Implement plugin logic (panel, datasource, app)
  4. Build with npm run build
  5. Test in Grafana instance
  6. Deploy to Grafana Plugin Registry (optional)

10. Signing Plugins

Field Description Value
plugin.json Plugin manifest file { "signature": "unsigned" }
grafana-sign CLI tool for signing plugins grafana-sign sign --pluginDir ./my-plugin

Example: Signing a Plugin

grafana-sign sign --pluginDir ./my-plugin --keyFile /path/to/key.pem --certFile /path/to/cert.pem
Note: The signed plugin will have a signature field set to valid in its manifest.
Warning: Unsigned plugins will not be loaded in Grafana environments with strict security policies.
Signature Status Description Impact
unsigned No signature present Plugin not loaded in secure environments
valid Signature verified successfully Plugin loaded normally
invalid Signature verification failed Plugin not loaded, error logged

Was this article helpful?