This example demonstrates how to build a basic app plugin for Grafana that uses custom routing.
App plugins can let you create a custom out-of-the-box monitoring experience by custom pages, nested datasources and panel plugins.
Example: utils.routing.ts, constants.ts#L17
You can enable the tab navigation bar for your app plugin by passing a "nav-model" the onNavChanged()
function that is passed in as a prop to your root App component.
If you don't want to get too much into the details you can just edit NAVIGATION
object in constants.ts#L17.
Setting the tabs in constants.ts#L17
// Add navigation items here that you want to appear in the tabs
export const NAVIGATION: Record<string, NavItem> = {
[ROUTES.One]: {
id: ROUTES.One,
text: 'Page One',
icon: 'database',
url: `${PLUGIN_BASE_URL}/one`,
},
// ...
};
Example: Routes.tsx, constants.ts#L6
Grafana (and the app plugins are no exception) is using React Router. In order to register a new route based on our examples just add a new route constant in constants.ts#L6 and use it in the Routes.tsx.
Example: Routes.tsx, PageThree.tsx
In order use URL parameters for any routes just append them to the path in the <Route>
component.
// URL parameters are identified by the colon syntax (":<parameter>")
// The "?" at the end marks the parameter as optional, so React Router will also identify this route if there are no parameters used.
<Route exact path={`${PLUGIN_BASE_URL}/${ROUTES.Three}/:id?`} component={PageThree} />
Retrieving URL parameters:
import { useParams } from 'react-router-dom';
export const MyComponent = () => {
const { id } = useParams<{ id: string }>();
};
Example: utils.routing.ts#L25
In order to hide the tab navigation bar and have access to the full view right to the Grafana sidebar just call the onNavChanged()
function with an undefined
(as it is done in utils.routing.ts#L25).
In the current example it is enough to just exclude your route from the NAVGITAION
object (constants.ts#L17), and it will be automatically used as a full-width page.
Example: PageFour.tsx
We suggest you to use Emotion to style your components, just as in the example above.
More info on how to use @emotion/css
Example: PageFour.tsx#L25
The easiest way is to use the useStyles2()
hook to access the GrafanaTheme2
theme object.
Example: plugin.json
You can define pages that you want to add to the left sidebar menu under the includes
section of your plugin.json.
Structure of a page item:
// plugin.json
{
"includes": [
{
"type": "page",
// The text of the menu item
"name": "Page One",
// The link of the menu item (%PLUGIN_ID% will resolve the id of your plugin at build time)
"path": "/a/%PLUGIN_ID%",
// The role who can access this page
"role": "Admin",
// This tells Grafana to add this page to the left sidebar
"addToNav": true,
"defaultNav": true
}
]
}
Example: module.ts, AppConfig.tsx
A configuration page can be used to set custom configuration options that are going to be persisted for your plugin on the backend. It can also be used to save secrets that are no longer sent down to the client but can be appended to your proxied requests by the backend.
Example: AppConfig.tsx
Add a new form field under your AppConfig.tsx and make sure that you are setting it under the jsonData
object.
The jsonData
object is an arbitrary object of data that can be persisted for your plugin using the /api/plugins/${pluginId}/settings
API endpoint.
Example: AppConfig.tsx
Secrets for plugins are stored in the secureJsonData
object.
This is an arbitrary object of data, however its value is never going to be sent back to the frontend for security reasons.
Example: AppConfig.tsx
Updating plugin settings can be done by sending a POST
request to the /api/plugins/${pluginId}/settings
API endpoint
Example payload:
{
enabled: true,
pinned: true,
// Arbitrary object of data for your plugin
jsonData: {
apiUrl: state.apiUrl,
isApiKeySet: true,
},
// Arbitrary object of data for plugin secrets
// (pass `undefined` if you don't want to override existing values)
secureJsonData: {
apiKey: state.apiKey,
}
}
Below you can find source code for existing app plugins and other related documentation.