Claude Skills Guide

Claude Code for Grafana Plugin Development Workflow

Grafana plugins extend Grafana’s capabilities with custom visualizations, data sources, and panels. Developing these plugins involves multiple steps—from scaffolding to debugging—and Claude Code can significantly accelerate each phase. This guide shows you how to create a Claude skill tailored for Grafana plugin development, complete with practical examples and workflow patterns.

Why Use Claude Code for Grafana Plugin Development

Grafana plugin development requires knowledge of React, TypeScript, the Grafana SDK, and often backend Go or Python code for data source plugins. The complexity can be overwhelming, especially when switching between different plugin types (data source, panel, app, or scene).

Claude Code helps by:

A well-designed skill centralizes these capabilities, making plugin development faster and more consistent.

Creating a Grafana Plugin Development Skill

The first step is creating a skill file that captures the plugin development workflow. Save this as grafana-plugin-dev.md in your skills directory.

---
name: Grafana Plugin Developer
description: Expert assistant for Grafana plugin development, including data sources, panels, and apps
---

# Grafana Plugin Development Assistant

You help developers build Grafana plugins using the official Grafana Plugin SDK.

## Available Commands

- "scaffold a new panel plugin" → Create basic plugin structure
- "explain the plugin.json schema" → Document plugin configuration
- "write a simple data source query" → Generate query executor code
- "add a new panel option" → Extend panel configuration

This minimal skill declaration gives Claude context about your development workflow. You can expand it with more specific commands as your needs grow.

Scaffolding a New Plugin

When starting a new Grafana plugin, you typically use @grafana/create-plugin. Claude can guide you through this process or generate the necessary files manually if you need more control.

Here’s a workflow for scaffolding a panel plugin:

# Create a new panel plugin using the official tool
npx @grafana/create-plugin@latest my-panel-plugin --template panel

After scaffolding, you’ll have a directory structure like this:

my-panel-plugin/
├── src/
│   ├── plugin.json      # Plugin manifest
│   ├── module.ts        # Entry point
│   ├── Panel.tsx        # Main panel component
│   └── types.ts         # TypeScript types
├── package.json
└── tsconfig.json

Claude can help you customize each file. For example, to add a custom option to your panel, ask Claude to generate the options schema and the corresponding React component.

Writing Panel Plugin Code

A typical panel plugin needs three main components: the options schema, the panel component, and the module registration. Here’s how Claude helps you write each.

Panel Options Schema

Define your panel’s configurable options using the Grafana schema:

import { PanelOptionsSerializer } from './types';

export const defaults: PanelOptionsSerializer = {
  showLegend: true,
  lineWidth: 2,
  fillOpacity: 10,
  gradientMode: 'none',
  legendDisplayMode: 'list',
};

export const schema: PanelOptionsSerializer = {
  type: 'object',
  properties: {
    showLegend: { type: 'boolean', defaultValue: true },
    lineWidth: { type: 'number', defaultValue: 2, minimum: 0, maximum: 10 },
    fillOpacity: { type: 'number', defaultValue: 10, minimum: 0, maximum: 100 },
    gradientMode: {
      type: 'string',
      defaultValue: 'none',
      enum: ['none', 'opacity', 'hue', 'scheme'],
    },
    legendDisplayMode: {
      type: 'string',
      defaultValue: 'list',
      enum: ['list', 'table', 'hidden'],
    },
  },
};

Panel Component

The React component renders your visualization:

import React, { useMemo } from 'react';
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from './types';

export const SimplePanel: React.FC<PanelProps<SimpleOptions>> = ({
  data,
  options,
  width,
  height,
}) => {
  const processedData = useMemo(() => {
    // Transform time series data for rendering
    return data.series.map(series => ({
      name: series.name,
      points: series.fields[0].values.toArray(),
    }));
  }, [data]);

  return (
    <div style={{ width, height }}>
      {/* Your visualization rendering logic here */}
    </div>
  );
};

Claude can generate boilerplate like this instantly, letting you focus on the actual visualization logic.

Data Source Plugin Development

Data source plugins require additional components: query executors, config pages, and backend code. Here’s a typical workflow.

Query Executor

The query executor runs when Grafana requests data:

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

export async function query(
  request: DataQueryRequest<MyQuery>
): Promise<DataQueryResponse> {
  const { range, targets } = request;
  
  // Process each query target
  const results = await Promise.all(
    targets.map(async (target) => {
      const response = await fetchData(target, range);
      return transformResponse(response, target);
    })
  );

  return { data: results };
}

async function fetchData(query: MyQuery, range: TimeRange) {
  // Your API call logic here
  const url = `${query.url}/query?from=${range.from}&to=${range.to}`;
  return fetch(url).then(res => res.json());
}

Config Page

Users configure your data source through a config page:

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

export const ConfigEditor: React.FC<DataSourcePluginOptionsEditorProps<MyDataSourceOptions>> = ({
  options,
  onOptionsChange,
}) => {
  return (
    <div>
      <div className="gf-form">
        <label>API URL</label>
        <input
          type="text"
          value={options.jsonData.url || ''}
          onChange={(e) =>
            onOptionsChange({
              ...options,
              jsonData: { ...options.jsonData, url: e.target.value },
            })
          }
        />
      </div>
    </div>
  );
};

Testing and Debugging

Claude helps you debug plugin issues by reading logs, explaining error messages, and suggesting fixes. Common issues include:

Plugin not loading: Check plugin.json for valid id, type, and info fields. Ensure the module.js path is correct.

Data query failures: Verify your query executor handles the request structure correctly. Add console logs to trace execution:

console.log('Query request:', request);
try {
  const result = await query(request);
  console.log('Query result:', result);
  return result;
} catch (error) {
  console.error('Query failed:', error);
  throw error;
}

TypeScript errors: Run npm run build to see compilation errors. Claude can explain type mismatches and suggest fixes.

Best Practices for Plugin Development with Claude

  1. Start with the official scaffolding rather than writing from scratch—this ensures compatibility with Grafana’s plugin infrastructure.

  2. Use TypeScript throughout—Grafana’s type definitions are comprehensive, and Claude uses them to generate accurate code.

  3. Keep options minimal—only expose settings that users need to customize. Claude can help you refactor complex options into simpler schemas.

  4. Test incrementally—run npm run dev and check your plugin in a local Grafana instance after each change.

  5. Document your skill—if you create custom commands for your workflow, document them clearly so Claude knows when to use each pattern.

Conclusion

Claude Code transforms Grafana plugin development from a complex, multi-step process into a guided workflow. By creating a dedicated skill for plugin development, you get instant access to scaffolding, code generation, and debugging assistance—all tailored to the Grafana plugin SDK. Start with a simple skill definition and expand it as you encounter new plugin patterns. The result is faster development cycles and more reliable plugins.

Built by theluckystrike — More at zovo.one