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.
-
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
-
Go to the directory of your newly created plugin:
cd <your-plugin>
-
Install the dependencies:
npm install
-
Build the plugin:
npm run dev
-
Start Grafana:
docker compose up
- 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>
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
, andapp
.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.
- Start your Grafana instance with
docker compose up
. - Open Grafana in your browser. Go to http://localhost:3000.
- 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:
-
In
PageOne.tsx
, change some of the page text content:src/pages/PageOne.tsx<PluginPage>New page content</PluginPage>
-
Save the file.
-
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:
-
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
}
] -
Save the
src/plugin.json
file. -
Restart your Grafana instance.
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.
-
Create a new file called
src/pages/NewPage.tsx
and add the following code:src/pages/NewPage.tsximport React from 'react';
import { PluginPage } from '@grafana/runtime';
export function NewPage() {
return <PluginPage>New Page</PluginPage>;
} -
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 />} />; -
Save the file.
-
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.
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.
"dependencies": {
"plugins": [
{
"type": "panel",
"name": "Clock Panel",
"id": "grafana-clock-panel",
"version": "^2.1.0"
}
]
}
Next steps
- Sign your plugin
- Publish your plugin
- Write e2e tests for your plugin