Skip to main content

Build an app plugin

Introduction

In this tutorial you will learn how to create an app plugin. App plugins are Grafana plugins that allow you to bundle data sources and panel plugins within a single package. They also enable you to create custom pages within Grafana, providing a dedicated space for documentation, sign-up forms, integration with other services via HTTP. You can also use app plugins to create Scenes apps.

App plugins can be displayed in the navigation menu and offer the flexibility to define UI extensions.

Prerequisites

  • Grafana v9.0 or later
  • LTS version of Node.js

Create a new app plugin

The Grafana create-plugin tool is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The tool scaffolds a starter plugin, all the required configuration, and a development environment using Docker Compose for you.

  1. In a new directory, create a plugin from a template using the create-plugin tool. When prompted for the kind of plugin, select app:

    npx @grafana/create-plugin@latest
  2. Go to the directory of your newly created plugin:

    cd <your-plugin>
  3. Install the dependencies:

    npm install
  4. Build the plugin:

    npm run dev
  5. Start Grafana:

    docker compose up
  6. Open Grafana, by default http://localhost:3000/, and then go to Administration > Plugins. Make sure that your app plugin is there.

You can also verify that Grafana has discovered your plugin by checking the logs:

INFO[01-01|12:00:00] Plugin registered       logger=plugin.loader pluginID=<your-plugin>
note

If you choose to have a backend for your app plugin, you must build the backend plugin binary by running mage -v before starting Grafana with Docker.

Anatomy of a plugin

Every plugin you create requires at least two files: plugin.json and src/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 follow this naming convention: <$organization-name>-<$plugin-name>-<$plugin-type>. The create-plugin tool correctly configures this based on your responses to its prompts.

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.js file, the entrypoint for your plugin. module.js exposes the implementation of your plugin, which depends on the type of plugin you're building.

Specifically, src/module.ts needs to export a class that extends GrafanaPlugin, and can be any of the following:

Development workflow

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

The first step is to see your scaffolded plugin in action.

  1. Start your Grafana instance with docker compose up.
  2. Open Grafana in your browser. Go to http://localhost:3000.
  3. Go to Apps -> Your App Name.

Now that you can view your app root page (page one in the example), try making a change to the app plugin:

  1. In PageOne.tsx, change some of the page text content:

    src/pages/PageOne.tsx
    <PluginPage>New page content</PluginPage>
  2. Save the file.

  3. Reload Grafana in your browser. Your changes should appear.

Add a page in the navigation menu

To add pages to the navigation menu under your app menu item, modify the plugin.json file's includes section.

When you scaffold your plugin, the create-plugin tool adds some example pages to the navigation menu. Each example page follows a path such as /a/%PLUGIN_ID%/PAGE_NAME. Any requests sent to /a/%PLUGIN_ID%, for example, /a/myorgid-simple-app/, are routed to the root page of the app plugin. The root page is a React component that returns the content for a given route.

Let's add a new page to the navigation menu:

  1. Modify plugin.json to add a new page.

    src/plugin.json
    // ...
    "includes": [
    // ...
    {
    "type": "page",
    "name": "New Page",
    "path": "/a/%PLUGIN_ID%/new-page",
    "roles": "Admin",
    "addToNav": true,
    "defaultNav": false
    }
    ]
  2. Save the src/plugin.json file.

  3. Restart your Grafana instance.

note

After saving the plugin.json file, you need to restart your Grafana instance to see the new page in the navigation menu.

The new page appears in the navigation menu. You can now edit the React router in src/components/App/App.tsx and point a custom component to it.

  1. Create a new file called src/pages/NewPage.tsx and add the following code:

    src/pages/NewPage.tsx
    import React from 'react';
    import { PluginPage } from '@grafana/runtime';

    export function NewPage() {
    return <PluginPage>New Page</PluginPage>;
    }
  2. Modify the routes in src/components/App/App.tsx to recognize the new page:

    src/components/App/App.tsx
    {
    /* .... */
    }
    <Route path="new-page" element={<NewPage />} />;
  3. Save the file.

  4. Reload Grafana to see the new page in place.

You don't need to register all your pages inside includes in your plugin.json. Register only pages that you want to add to the navigation menu.

note

You can only have one level of pages in the navigation menu. Sub-menu items are not supported.

Configuration page

You can add a configuration page to your app plugin where users can configure any settings your plugin needs. Your plugin should already have an example configuration page with its source code located in src/components/AppConfig/AppConfig.tsx.

Save user settings

To store user settings, send a POST request to /api/plugins/%PLUGIN_ID%/settings with the jsonData and secureJsonData as data.

export const updatePluginSettings = async (pluginId: string, data: Partial<PluginMeta>) => {
const response = await getBackendSrv().fetch({
url: `/api/plugins/${pluginId}/settings`,
method: 'POST',
data, // data: { jsonData: { ... }, secureJsonData: { ... } }
});

return lastValueFrom(response);
};

Retrieve user settings

The user settings are part of the plugin meta. You can retrieve them inside a React component by using the usePluginContext hook. For example:

import React from 'react';
import usePluginContext from '@grafana/data';

function MyComponent() {
const context = usePluginContext();
// user settings
const jsonData = context.meta.jsonData;
}

Fetch data from frontend code using the data proxy

If you want to fetch data from your app frontend code (for example, from a thid party API) without CORS issues or using authenticated requests, then you can use the data proxy.

Add nested plugins inside your app

You can nest data sources and panel plugins inside your app plugins. See Work with nested plugins.

Include external plugins

If you want to let users know that your app requires an existing plugin, you can add it as a dependency in plugin.json. Note that they'll still need to install it themselves.

src/plugin.json
"dependencies": {
"plugins": [
{
"type": "panel",
"name": "Clock Panel",
"id": "grafana-clock-panel",
"version": "^2.1.0"
}
]
}

Next steps