grafana-scenes
Build Grafana plugin pages using the @grafana/scenes framework. Use when creating new scene pages, adding panels/visualizations, setting up drilldown navigation, defining variables, configuring query runners, building table/timeseries/stat panels, or extending SceneObjectBase for custom scene objects. Triggers on any work involving SceneApp, SceneAppPage, EmbeddedScene, SceneQueryRunner, SceneDataTransformer, PanelBuilders, SceneFlexLayout, QueryVariable, or drilldown/tab configuration in Grafana plugins.
Works with
---
name: grafana-scenes
description: Build Grafana plugin pages using the @grafana/scenes framework. Use when creating new scene pages, adding panels/visualizations, setting up drilldown navigation, defining variables, configuring query runners, building table/timeseries/stat panels, or extending SceneObjectBase for custom scene objects. Triggers on any work involving SceneApp, SceneAppPage, EmbeddedScene, SceneQueryRunner, SceneDataTransformer, PanelBuilders, SceneFlexLayout, QueryVariable, or drilldown/tab configuration in Grafana plugins.
license: Apache-2.0
---
# @grafana/scenes Framework
Build reactive, data-driven Grafana plugin pages with declarative scene objects.
## Core Concepts
Scenes composes a tree of objects: `SceneApp` → `SceneAppPage` → `EmbeddedScene` → layouts → panels. Each node can own data (`$data`), variables (`$variables`), time ranges (`$timeRange`), and behaviors (`$behaviors`) that propagate down the tree.
## Quick Start: New Scene Page
### 1. Create the scene file
```typescript
// src/components/scenes/MyFeature/scene.tsx
import {
EmbeddedScene, SceneFlexLayout, SceneFlexItem,
SceneQueryRunner, SceneVariableSet, QueryVariable,
PanelBuilders, VariableValueSelectors, SceneControlsSpacer,
} from '@grafana/scenes';
export function getMyFeatureScene(params: { datasource: DataSourceRef }) {
const queryRunner = new SceneQueryRunner({
datasource: params.datasource,
queries: [{ refId: 'A', expr: 'up{cluster=~"$cluster"}', instant: true, format: 'table' }],
});
const panel = PanelBuilders.table()
.setData(queryRunner)
.setTitle('My Table')
.build();
return new EmbeddedScene({
$variables: new SceneVariableSet({
variables: [
new QueryVariable({
name: 'cluster',
query: 'label_values(up, cluster)',
datasource: params.datasource,
isMulti: true, includeAll: true, defaultToAll: true,
}),
],
}),
controls: [new VariableValueSelectors({}), new SceneControlsSpacer()],
body: new SceneFlexLayout({
direction: 'column',
children: [new SceneFlexItem({ body: panel })],
}),
});
}
```
### 2. Create the page
```typescript
// src/components/scenes/MyFeature/MyFeature.tsx
import { SceneAppPage, SceneTimeRange } from '@grafana/scenes';
export function getMyFeaturePage(params) {
return new SceneAppPage({
title: 'My Feature',
url: '/a/my-plugin-id/my-feature',
routePath: 'my-feature/*',
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }),
getScene: () => getMyFeatureScene(params),
drilldowns: [],
});
}
```
### 3. Register in SceneApp
Add the page to the `SceneApp` pages array in the root scene file.
## Key Patterns
### Drilldowns (click-through navigation)
```typescript
drilldowns: [{
routePath: ':cluster/*',
getPage: (match, parent) => new SceneAppPage({
title: decodeURIComponent(match.params.cluster),
url: `${parent.state.url}/${match.params.cluster}`,
routePath: `${match.params.cluster}/*`,
getScene: () => detailScene(decodeURIComponent(match.params.cluster)),
}),
}]
```
### Tabs (sub-pages within a detail view)
Pass `tabs: [SceneAppPage, ...]` instead of `getScene` on a `SceneAppPage`. Each tab is itself a `SceneAppPage` with its own scene.
### Query with transformations
Wrap a `SceneQueryRunner` in `SceneDataTransformer` to apply Grafana transforms or custom RxJS operators:
```typescript
new SceneDataTransformer({
$data: queryRunner,
transformations: [
{ id: 'organize', options: { renameByName: { 'Value #A': 'CPU' } } },
(ctx) => (source) => source.pipe(map((frames) => /* custom transform */)),
],
})
```
### Custom scene object
Extend `SceneObjectBase` with a static `Component` for custom interactive UI:
```typescript
class MyWidget extends SceneObjectBase<MyWidgetState> {
static Component = ({ model }: SceneComponentProps<MyWidget>) => {
const state = model.useState();
return <div>{state.value}</div>;
};
}
```
### Table column overrides
Build `ConfigOverrideRule` objects for drill-down links, filtering, units, widths, custom cells.
### Panel types
`PanelBuilders.table()`, `.timeseries()`, `.stat()`, `.gauge()`, `.barchart()` — chain `.setData()`, `.setTitle()`, `.setUnit()`, `.setOption()`, `.setOverrides()`, then `.build()`.
## Common Pitfalls
- Always use `routePath: 'path/*'` (with wildcard) on pages that have drilldowns or tabs
- `encodeURIComponent`/`decodeURIComponent` URL params — K8s names can contain `/`
- Variables referenced in queries as `$varName` must exist in an ancestor `SceneVariableSet`
- `getScene` is called lazily; don't create side effects in the factory
- For instant queries, set both `instant: true` and `format: 'table'`
## Resources
- [@grafana/scenes Documentation](https://grafana.com/docs/grafana/latest/developers/plugins/create-plugin-ui/grafana-scenes/)
- [@grafana/scenes npm](https://www.npmjs.com/package/@grafana/scenes)
- [Grafana Plugin Tools](https://grafana.com/developers/plugin-tools/)More Accessibility skills
skill-creator
anthropics/skills
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
ui-ux-pro-max
nextlevelbuilder/ui-ux-pro-max-skill
UI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation. Searchable local data: 79 searchable styles (50 active), 192 product palettes and reasoning profiles, 74 font pairings, 119 UX guidelines, 105 icons, 17 GSAP presets, 25 chart types, and 22 stacks.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the `data-*` timing attributes, `class="clip"`, tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Also covers Tailwind projects and the STORYBOARD.md / SCRIPT.md plan formats. Read before writing composition HTML.

