> Create Vega and Vega-Lite visualizations with ES|QL data sources in Kibana. Use when building custom charts, dashboards, or programmatic panel layouts beyond standard Lens charts.
npx skills add https://github.com/elastic/agent-skills --skill kibana-vega
Create and manage Kibana dashboards and Vega visualizations with ES|QL data sources.
Vega is a declarative visualization grammar for creating custom charts in Kibana. Combined with ES|QL queries, it
enables highly customized visualizations beyond standard Kibana charts.
Important Version Requirement: This skill strictly supports ES|QL data sources and requires **Serverless Kibana
or version 9.4+ (SNAPSHOT)**. It will not work reliably on older versions or with older Lucene/KQL data source
definitions.
Kibana connection is configured via environment variables. Run node scripts/kibana-vega.js test to verify the
connection. If the test fails, suggest these setup options to the user, then stop. Do not try to explore further until a
successful connection test.
export KIBANA_CLOUD_ID="deployment-name:base64encodedcloudid"
export KIBANA_API_KEY="base64encodedapikey"
export KIBANA_URL="https://your-kibana:5601"
export KIBANA_API_KEY="base64encodedapikey"
export KIBANA_URL="https://your-kibana:5601"
export KIBANA_USERNAME="elastic"
export KIBANA_PASSWORD="changeme"
For local development and testing, use start-local to quickly spin up
Elasticsearch and Kibana using Docker or Podman:
curl -fsSL https://elastic.co/start-local | sh
After installation completes, Elasticsearch runs at http://localhost:9200 and Kibana at http://localhost:5601. The
script generates a random password for the elastic user, stored in the .env file inside the created
elastic-start-local folder.
To configure the environment variables for this skill, source the .env file and export the connection settings:
source elastic-start-local/.env
export KIBANA_URL="$KB_LOCAL_URL"
export KIBANA_USERNAME="elastic"
export KIBANA_PASSWORD="$ES_LOCAL_PASSWORD"
Then run node scripts/kibana-vega.js test to verify the connection.
export KIBANA_INSECURE="true"
# Test connection
node scripts/kibana-vega.js test
# Create visualization directly from stdin (no intermediate file needed)
echo '<json-spec>' | node scripts/kibana-vega.js visualizations create "My Chart" -
# Get visualization spec for review/modification
node scripts/kibana-vega.js visualizations get <vis-id>
# Update visualization from stdin
echo '<json-spec>' | node scripts/kibana-vega.js visualizations update <vis-id> -
# Create dashboard
node scripts/kibana-vega.js dashboards create "My Dashboard"
# Add visualization with grid position
node scripts/kibana-vega.js dashboards add-panel <dashboard-id> <vis-id> --x 0 --y 0 --w 24 --h 15
# Apply a complete layout from stdin
echo '<layout-json>' | node scripts/kibana-vega.js dashboards apply-layout <dashboard-id> -
Note: Use - as the file argument to read JSON from stdin. This enables direct spec creation without intermediate
files.
IMPORTANT: Always use proper JSON format (not HJSON with triple quotes) to avoid parse errors.
{
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"title": "My Chart",
"autosize": { "type": "fit", "contains": "padding" },
"config": {
"axis": { "domainColor": "#444", "tickColor": "#444" },
"view": { "stroke": null }
},
"data": {
"url": {
"%type%": "esql",
"query": "FROM logs-* | STATS count = COUNT() BY status | RENAME status AS category"
}
},
"mark": { "type": "bar", "color": "#6092C0" },
"encoding": {
"x": { "field": "category", "type": "nominal" },
"y": { "field": "count", "type": "quantitative" }
}
}
| Property | Description |
| --------------------------- | ------------------------------------------ | --------- |
| %type%: "esql" | Required. Use ES | QL parser |
| %context%: true | Apply dashboard filters |
| %timefield%: "@timestamp" | Enable time range with ?_tstart/?_tend |
# Create visualization directly from JSON
echo '{"$schema":"https://vega.github.io/schema/vega-lite/v6.json",...}' | \
node scripts/kibana-vega.js visualizations create "My Chart" -
# Update visualization
echo '{"$schema":...}' | node scripts/kibana-vega.js visualizations update <id> -
# Apply layout directly
echo '{"panels":[{"visualization":"<id>","x":0,"y":0,"w":24,"h":10}]}' | \
node scripts/kibana-vega.js dashboards apply-layout <dash-id> -
Kibana dashboards use a 48-column grid:
| Width | Columns | Use Case |
| ------- | ------- | -------------------------------- |
| Full | 48 | Timelines, heatmaps, wide charts |
| Half | 24 | Side-by-side comparisons |
| Third | 16 | Three-column layouts |
| Quarter | 12 | KPI metrics, small summaries |
Primary information must be visible without scrolling.
| Resolution | Visible Height | Layout Budget |
| ---------- | -------------- | -------------------------- |
| 1080p | ~30 units | 2 rows: h:10 + h:12 |
| 1440p | ~40 units | 3 rows: h:12 + h:12 + h:12 |
Height guidelines:
h: 10 — Compact bar charts (≤7 items), fits above foldh: 12-13 — Standard charts, timelinesh: 15+ — Detailed views, use below fold┌───────────────────────┬───────────────────────┐ y:0
│ Current State A │ Current State B │ h:10 (compact)
├───────────────────────┴───────────────────────┤ y:10
│ Primary Timeline │ h:12 (main trend)
├ ─ ─ ─ ─ ─ ─ ─ FOLD ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ y:22 (1080p fold)
│ Secondary Timeline │ h:12 (below fold OK)
├───────────────────────┬───────────────────────┤ y:34
│ Complementary 1 │ Complementary 2 │ h:10
└───────────────────────┴───────────────────────┘
# Row 1: Two compact half-width charts (above fold)
node scripts/kibana-vega.js dashboards add-panel $DASH $VIS1 --x 0 --y 0 --w 24 --h 10
node scripts/kibana-vega.js dashboards add-panel $DASH $VIS2 --x 24 --y 0 --w 24 --h 10
# Row 2: Full-width timeline (above fold)
node scripts/kibana-vega.js dashboards add-panel $DASH $VIS3 --x 0 --y 10 --w 48 --h 12
# Row 3: Below fold content
node scripts/kibana-vega.js dashboards add-panel $DASH $VIS4 --x 0 --y 22 --w 48 --h 12
Create layout.json:
{
"title": "My Dashboard",
"panels": [
{ "visualization": "<vis-id-1>", "x": 0, "y": 0, "w": 24, "h": 10 },
{ "visualization": "<vis-id-2>", "x": 24, "y": 0, "w": 24, "h": 10 },
{ "visualization": "<vis-id-3>", "x": 0, "y": 10, "w": 48, "h": 12 },
{ "visualization": "<vis-id-4>", "x": 0, "y": 22, "w": 48, "h": 12 }
]
}
Apply it:
node scripts/kibana-vega.js dashboards apply-layout <dashboard-id> layout.json
''' multi-line strings cause parse errors in Kibana; use single-linequeries with escaped quotes \"
room.name breaks Vega (interpreted as nested path); use ES|QL RENAME room.name AS roomautosize: { type: fit, contains: padding }axis: { "labelLimit": 150 }SORT field DESC and use sort: null in encoding (preserves dataorder); avoid sort: "-x" in layered specs (bar + text labels) as it causes "conflicting sort properties" warnings
axis: { "labelAngle": 0, "tickCount": 8 }, let Vega auto-format datestitle: null onaxes
#6092C0) for bar chartswhere position already encodes value; reserve color encoding for categorical distinction (e.g., multiple lines in a
time series)
"config": {
"axis": { "domainColor": "#444", "tickColor": "#444" },
"view": { "stroke": null }
}
# Dashboards
node scripts/kibana-vega.js dashboards list [search]
node scripts/kibana-vega.js dashboards get <id>
node scripts/kibana-vega.js dashboards create <title>
node scripts/kibana-vega.js dashboards delete <id>
node scripts/kibana-vega.js dashboards add-panel <dash-id> <vis-id> [--x N] [--y N] [--w N] [--h N]
node scripts/kibana-vega.js dashboards apply-layout <dash-id> <file|->
# Visualizations (use - for stdin instead of file)
node scripts/kibana-vega.js visualizations list [vega]
node scripts/kibana-vega.js visualizations get <id>
node scripts/kibana-vega.js visualizations create <title> <file|->
node scripts/kibana-vega.js visualizations update <id> <file|->
node scripts/kibana-vega.js visualizations delete <id>
practices
parameters
| Error | Solution |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| "End of input while parsing an object" | Don't use HJSON ''' triple-quotes; use JSON with single-line queries |
| Labels show "undefined" | Rename dotted fields: RENAME room.name AS room |
| Bars invisible / not rendering | Remove complex scale.domain, use simpler color schemes |
| Y-axis labels truncated | Add axis: { "labelLimit": 150 } to encoding |
| Panels stacked vertically | Use --x --y --w --h options or apply-layout command |
| "width/height ignored" | Remove dimensions, use autosize |
| Bright white borders on dark theme | Add config: { "view": { "stroke": null }, "axis": { "domainColor": "#444", "tickColor": "#444" } } |
| "401 Unauthorized" | Check KIBANA_USERNAME/PASSWORD |
| "conflicting sort properties" | Don't use sort: "-x" in layered specs; pre-sort in ES\|QL and use sort: null |
| "404 Not Found" | Verify dashboard/visualization ID |
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
Picks random winners from lists, spreadsheets, or Google Sheets for giveaways, raffles, and contests. Ensures fair, unbiased selection with transparency.
Query openFDA API for drugs, devices, adverse events, recalls, regulatory submissions (510k, PMA), substance identification (UNII), for FDA regulatory data analysis and safety research.
MATLAB and GNU Octave numerical computing for matrix operations, data analysis, visualization, and scientific computing. Use when writing MATLAB/Octave scripts for linear algebra, signal processing, image processing, differential equations, optimization, statistics, or creating scientific visualizations. Also use when the user needs help with MATLAB syntax, functions, or wants to convert between MATLAB and Python code. Scripts can be executed with MATLAB or the open-source GNU Octave interpreter.
UMAP dimensionality reduction. Fast nonlinear manifold learning for 2D/3D visualization, clustering preprocessing (HDBSCAN), supervised/parametric UMAP, for high-dimensional data.
Creating interactive data visualisations using d3.js. This skill should be used when creating custom charts, graphs, network diagrams, geographic visualisations, or any complex SVG-based data visualisation that requires fine-grained control over visual elements, transitions, or interactions. Use this for bespoke visualisations beyond standard charting libraries, whether in React, Vue, Svelte, vanilla JavaScript, or any other environment.
Access AlphaFold 200M+ AI-predicted protein structures. Retrieve structures by UniProt ID, download PDB/mmCIF files, analyze confidence metrics (pLDDT, PAE), for drug discovery and structural biology.
Take elastic/kibana-vega from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.