npx skills add ...
npx skills add splunk/splunk-agent-skills --skill custom-visualization-builder
Scaffold, build, package, and install a custom visualization into Splunk using the dashboard-studio-extension framework. Use when the user wants to create a new custom viz, add a visualization to an existing project, or migrate a legacy custom viz.
npx skills add splunk/splunk-agent-skills --skill custom-visualization-builder
Scaffold, build, package, and install a custom Splunk dashboard visualization using the @splunk/dashboard-studio-extension framework. Vizzes run in a sandboxed iframe and receive data from Splunk searches. You can build them with plain JavaScript or React — pick based on what the viz needs.
No local Splunk instance is required for development. Build and package locally, then install the .spl into a supported instance. Requires Splunk Enterprise 10.4+ or Splunk Cloud Platform (equivalent release).
Use this skill when a user asks to create a new Splunk custom visualization, add a visualization to an existing dashboard-studio-extension project, package a visualization as a Splunk app, or migrate a legacy RequireJS custom viz to the current extension framework.
Do not install or update a visualization in a production or shared Splunk environment unless the user explicitly confirms that target. Do not ask users for raw credentials; use already configured local Splunk auth or a user-run install command.
JavaScript (@splunk/dashboard-studio-extension) | React (@splunk/dashboard-studio-extension/react) | |
|---|---|---|
| Use when | Canvas rendering, D3, plain DOM, minimal deps | Component-heavy UI, hooks-based state, most new projects |
| API style | Event listeners + getters | Hooks (useDataSources, useDimensions, useOptions, …) |
| Entry file | visualization.js | visualization.jsx |
| Extra deps | None | react, react-dom (peer deps, added by scaffold) |
A project is a Splunk app that can contain one or more visualizations under visualizations/. Each viz has its own directory with config.json, src/, and SPL.md.
The build system automatically discovers all directories under visualizations/ and builds them all. Name the project folder after the collection, not a single viz (e.g. splunk-custom-vizzes, not gauge).
Before generating any code, ask the user (or extract from context):
my_custom_viz.visualizations/. Only lowercase letters, numbers, and underscores ([a-z0-9_]). No dots, slashes, or spaces. Reject any value that would place files outside visualizations/<viz-slug>/. E.g. network_graph.app.conf.Apply the defaults in Accessibility & safe rendering unless the user's request or context makes them clearly inapplicable.
If the user's request is vague, ask before scaffolding.
Ask the user where to create the project if not specified. This can be any empty directory — no existing dashboard-enterprise repo required.
Adding a viz to an existing project: If a project already exists with build infrastructure, skip step 3 entirely. Just create visualizations/<viz-name>/ with config.json, src/visualization.js(x), src/visualization.css, and SPL.md. The build discovers all viz directories automatically — no registration needed. Then go to step 5.
The @splunk/create CLI currently requires interactive prompts for label, description, author, and template — only the project name can be passed as a positional arg. Full non-interactive support is planned. Ask the user to run the command and answer the remaining prompts:
"Please run this in your terminal and follow the prompts, then let me know when done:"
Use these values when prompted:
- Display label:
<Human Readable Label>- Description:
<description>- Author:
<author>- Template: JavaScript or React (per your framework choice above)
Then run
yarnto install dependencies.
Wait for the user to confirm the scaffold is complete before proceeding.
After scaffolding, verify package/app/app.conf has the correct app identity. The
scaffold generates this shape (values filled from your prompt answers):
App id: only letters, numbers, dots, underscores. Cannot start or end with a dot or underscore.
Leave
build = <@- buildNumber @>exactly as-is. That template tag is intentional — the packager (yarn package) substitutes a real build number when it stages the app. Do not replace it with a literal.
Then create SPL.md inside the viz directory (visualizations/<viz-name>/SPL.md):
visualizations/<viz-name>/config.json — see references/config-json.md for full field reference including optionsSchema and editorConfig:
JavaScript — visualization.js:
Data always arrives in columnar format: { fields: [{name}, ...], columns: [[col0val0, col0val1, ...], ...] }. All values are strings — parse numerics explicitly. Listener methods return a cleanup function.
React — visualization.jsx:
Call the hooks directly from your component.
Verify dist/<viz-name>/visualization.js is created. Hard-refresh the browser to see changes.
Output: dist/<app-id>-<version>-<hash>.spl
The packager auto-generates default/visualizations.conf (with framework_type = studio_visualization) and metadata/default.meta from the vizzes it finds in dist/.
Splunk Enterprise (10.4+): Apps > Manage Apps > Install app from file → select the .spl. No restart needed — the app loads immediately.
Splunk Cloud Platform: Private app installation requires the ACS CLI and AppInspect vetting. Do not direct Cloud users to the Splunk Web install UI — it will not work for private apps. Direct the user to the Splunk ACS documentation for the private app submission process.
Local Docker: Copy the .spl into the container, then prefer splunkctl
for the REST-backed install against the already-configured local test instance:
Splunkctl v0.1.0 requires --yes for this install POST. Use it only after the
user explicitly approves the exact local instance, package, update behavior,
and bounded non-production installation. It never authorizes a production or
shared-environment install.
If splunkctl is unavailable, say so and use the customer's explicitly
approved, customer-preferred existing local install route only when it preserves the same
non-production target and install scope. Never silently install or configure a
client, and keep production or shared-environment installation behind separate
explicit approval.
<app-id>.<viz-name>Before reporting the viz as complete, verify (unless the request made them inapplicable):
prefers-reduced-motionNew canvas viz from scratch:
"Build me a network graph visualization that shows connections between hosts."
Skill gathers requirements, asks the user to scaffold, creates visualization.js with canvas rendering, wires up data/dimensions/theme listeners, and packages the .spl.
Adding a viz to an existing project:
"Add a gauge viz to my existing custom viz project."
Skill skips scaffolding, creates visualizations/gauge/ with config.json, src/visualization.js, and SPL.md, and instructs the user to rebuild.
Migrating a legacy viz:
"Migrate my RequireJS custom viz to the new framework."
Skill reads the existing viz, maps updateView → render(), replaces SplunkVisualizationBase with VisualizationAPI listeners, removes RequireJS wrapper, and re-scaffolds the project.
For changes to visualization code only, copy the built JS directly — no reinstall needed:
Hard-refresh the browser. No Splunk restart needed.
For changes to app.conf, config.json, or adding a new viz, do a full reinstall (step 7–8).
Restart required for
showDrilldown: After enablingshowDrilldown: truein an already-installed app, Splunk must be restarted — the REST layer cachesconfig.jsonat startup, and reinstalling alone won't update the Interactions panel.
Vizzes should respond to Splunk's light/dark theme. The iframe is sandboxed — CSS custom properties from the host page are not inherited. Use hardcoded enterprise theme values.
JavaScript:
React:
These are defaults — apply them unless the user's request or context makes them clearly inapplicable.
Escape untrusted search values before writing to the DOM. Never use innerHTML or insertAdjacentHTML with raw field values from dataSources. Use textContent, createElement, or a sanitizing helper instead.
Make interactive elements keyboard-reachable. If clicking or hovering triggers behavior, ensure the element has tabIndex="0" and handles keydown for Enter/Space.
Provide text alternatives. Canvas elements and icon-only controls need an accessible label.
Meet WCAG AA contrast. Foreground text and icons must have at least 4.5:1 contrast against their background (3:1 for large text). The hardcoded THEME_COLORS values in Theme support already satisfy this for default text — use them as the baseline and verify any custom accent or status colors.
Respect prefers-reduced-motion. If the viz uses animation, check the media query and skip or minimise motion when it is set.
See references/canvas.md for sizing, interactivity, and drilldown patterns.
The scaffolded build.mjs does not auto-detect each viz's extension. Every
package.json script passes a single global --entry=<filename>, and the build applies
that one filename to every directory under visualizations/:
jsx: 'automatic' is enabled only when entryFileName ends in .jsx.
Consequences you must design around:
--entry=visualization.js) builds only visualization.js files; a
React-scaffolded project (--entry=visualization.jsx) builds only visualization.jsx.dist/ without an obvious error. If a viz doesn't appear in dist/, check
that its entry file matches the project's --entry extension.React-scaffolded project: just add visualizations/<viz>/src/visualization.jsx —
it builds automatically.
JavaScript-scaffolded project, no JS vizzes worth keeping: the simplest path is to
flip the whole project to .jsx. Add React (yarn add react react-dom), rename the
existing entry to visualization.jsx, and change every --entry=visualization.js to
--entry=visualization.jsx in package.json. jsx: 'automatic' turns on by itself.
You truly need both .js and .jsx vizzes in one project: the scaffolded
single---entry build can't do this. Replace the entry lookup in build.mjs with
per-viz resolution (prefer .jsx, fall back to .js) and always set
jsx: 'automatic':
Then drop the --entry flag from the package.json scripts. (This is a local
modification, not what @splunk/create produces.)
Avoid
@splunk/react-uiin the entry file. The scaffolded React template's default viz imports@splunk/react-uicomponents (Table,Paragraph,WaitSpinner), but those are pre-bundled CommonJS and their default exports do not survive the scaffold's esbuild ESM pipeline — the built viz throwsMinified React error #130(got an object instead of a component) on load. Build your UI with plain elements (or your own components). If you must use@splunk/react-ui, expect to add esbuild interop configuration tobuild.mjsfirst.
See references/migration.md for a full mapping from SplunkVisualizationBase to the new framework and step-by-step migration instructions.
See references/troubleshooting.md.
app.conf uses literal values except build = <@- buildNumber @> — that template tag is intentional and the packager fills it in. Don't replace it with a literal.id: only letters, numbers, dots, underscores. Cannot start or end with a dot or underscore.[a-z0-9_]. Reject values containing dots, slashes, or spaces. Confirm the resolved path is visualizations/<viz-slug>/ with no traversal.<link> tag neededsrc/ so the build inlines them as data URLs.setOptions is silently ignored in view mode — only works during edit modeoptionsSchema defaults are not guaranteed at runtimewindow.parent is not accessible and CSS custom properties from the host page are not inheritedreferences/api-reference.md — full API listing for JavaScript and Reactreferences/config-json.md — config.json field reference, optionsSchema, editorConfig editor typesreferences/drilldown-and-tokens.md — drilldown patterns and token read/writereferences/canvas.md — canvas sizing, interactivity, and drilldown patternsreferences/migration.md — migrating from legacy SplunkVisualizationBase vizzesreferences/troubleshooting.md — common errors and fixes