Develops plugins that plug into the Vite DevTools hub.
Install
mkdir -p .claude/skills/writing-vite-devtools-integrations && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4720" && unzip -o skill.zip -d .claude/skills/writing-vite-devtools-integrations && rm skill.zipInstalls to .claude/skills/writing-vite-devtools-integrations
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Creates devtools integrations for Vite using @vitejs/devtools-kit. Use when building Vite plugins with devtools panels, RPC functions, dock entries, shared state, or any devtools-related functionality. Applies to files importing from @vitejs/devtools-kit or containing devtools.setup hooks in Vite plugins.Key capabilities
- →Registers custom dock entries in Vite UI
- →Manages streaming terminal process output
- →Emits structured toasts and notifications
- →Binds command palette keyboard shortcuts
- →Integrates server-client RPC state
How it works
It hooks into the Vite devtools hub via the setup(ctx) method, extending the kit's context to manage terminal streams and custom dashboard panels.
Inputs & outputs
When to use writing-vite-devtools-integrations
- →Create a custom devtools panel
- →Add an RPC function to a Vite plugin
- →Manage shared state in Vite devtools
About this skill
Vite DevTools Kit
@vitejs/devtools-kit is the hub that unites many devtools integrations. It owns the cross-tool surface — docking, the command palette, terminal aggregation, cross-tool toasts — and wraps the framework-neutral Devframe container with the Vite-specific glue (Plugin.devtools.setup).
If you have a portable Devframe app already, drop it in via createPluginFromDevframe(d) from @vitejs/devtools-kit/node and the kit auto-derives the iframe dock entry. If you're authoring a Vite-specific integration that needs hub features directly, reach for Plugin.devtools.setup.
Core Concepts
A DevTools plugin extends a Vite plugin with a devtools.setup(ctx) hook. The context is the kit-augmented context (KitNodeContext extended with Vite-specific fields) — it carries Devframe's portable surface plus the hub-only subsystems the kit owns:
| Property | Layer | Purpose |
|---|---|---|
ctx.docks | kit | Register dock entries (iframe, action, custom-render, launcher, json-render) |
ctx.terminals | kit | Spawn and manage child processes with streaming terminal output |
ctx.messages | kit | Emit structured message entries and toast notifications |
ctx.commands | kit | Register executable commands with keyboard shortcuts and palette visibility |
ctx.rpc | devframe | Register RPC functions, broadcast to clients |
ctx.rpc.sharedState | devframe | Synchronized server-client state |
ctx.rpc.streaming | devframe | Streaming channels — chunk-style server↔client data with cancellation, replay, Web Streams interop |
ctx.views | devframe | Host static files for UI (hostStatic(base, distDir)) |
ctx.diagnostics | devframe | Structured diagnostics host (nostics) — register custom error codes |
ctx.createJsonRenderer | kit | Create server-side JSON render specs for zero-client-code UIs |
ctx.viteConfig | core | Resolved Vite configuration |
ctx.viteServer | core | Dev server instance (dev mode only) |
ctx.mode | devframe | 'dev' or 'build' |
Quick Start: Bridge a Devframe App
If you already have a portable Devframe definition, this is the one-liner. The kit synthesises the iframe dock entry from the definition's id / name / icon / basePath, mounts the SPA via views.hostStatic, runs the devtool's own setup, then runs the optional kit-only options.setup.
// vite.config.ts
import { createPluginFromDevframe } from '@vitejs/devtools-kit/node'
import devtool from './my-devtool'
export default {
plugins: [
createPluginFromDevframe(devtool, {
// Optional kit-only setup for hub features:
setup(ctx) {
ctx.commands.register({
id: 'my-devtool:clear-cache',
title: 'Clear Cache',
handler: () => {/* ... */},
})
},
}),
],
}
Quick Start: Minimal Hub-Native Plugin
When the integration is intrinsically tied to Vite (it inspects the resolved config, augments middleware, etc.), reach for Plugin.devtools.setup directly:
/// <reference types="@vitejs/devtools-kit" />
import type { Plugin } from 'vite'
export default function myPlugin(): Plugin {
return {
name: 'my-plugin',
devtools: {
setup(ctx) {
ctx.docks.register({
id: 'my-plugin',
title: 'My Plugin',
icon: 'ph:puzzle-piece-duotone',
type: 'iframe',
url: 'https://example.com/devtools',
})
},
},
}
}
Quick Start: Full Integration
/// <reference types="@vitejs/devtools-kit" />
import type { Plugin } from 'vite'
import { fileURLToPath } from 'node:url'
import { defineRpcFunction } from '@vitejs/devtools-kit'
export default function myAnalyzer(): Plugin {
const data = new Map<string, { size: number }>()
return {
name: 'my-analyzer',
// Collect data in Vite hooks
transform(code, id) {
data.set(id, { size: code.length })
},
devtools: {
setup(ctx) {
// 1. Host static UI
const clientPath = fileURLToPath(
new URL('../dist/client', import.meta.url)
)
ctx.views.hostStatic('/__my-analyzer/', clientPath)
// 2. Register dock entry
ctx.docks.register({
id: 'my-analyzer',
title: 'Analyzer',
icon: 'ph:chart-bar-duotone',
type: 'iframe',
url: '/__my-analyzer/',
})
// 3. Register RPC function
ctx.rpc.register(
defineRpcFunction({
name: 'my-analyzer:get-data',
type: 'query',
setup: () => ({
handler: async () => Array.from(data.entries()),
}),
})
)
},
},
}
}
Namespacing Convention
CRITICAL: Always prefix RPC functions, shared state keys, dock IDs, and command IDs with your plugin name:
// Good - namespaced
'my-plugin:get-modules'
'my-plugin:state'
'my-plugin:clear-cache' // command ID
// Bad - may conflict
'get-modules'
'state'
Dock Entry Types
| Type | Use Case |
|---|---|
iframe | Full UI panels, dashboards (most common) |
json-render | Server-side JSON specs — zero client code needed |
action | Buttons that trigger client-side scripts (inspectors, toggles) |
custom-render | Direct DOM access in panel (framework mounting) |
launcher | Actionable setup cards for initialization tasks |
Iframe Entry
ctx.docks.register({
id: 'my-plugin',
title: 'My Plugin',
icon: 'ph:house-duotone',
type: 'iframe',
url: '/__my-plugin/',
})
Iframes can also point at a remote-hosted URL that connects back via WebSocket, so you don't have to ship a SPA dist with your plugin:
ctx.docks.register({
id: 'my-remote-tool',
title: 'My Tool',
icon: 'ph:cloud-duotone',
type: 'iframe',
url: 'https://example.com/devtools',
remote: true, // or { transport: 'query', originLock: false }
})
On the hosted page, call connectRemoteDevTools() from @vitejs/devtools-kit/client to get a fully connected DevToolsRpcClient. Dev-mode only — auto-hidden in build mode. See Remote Client Patterns.
Action Entry
ctx.docks.register({
id: 'my-inspector',
title: 'Inspector',
icon: 'ph:cursor-duotone',
type: 'action',
action: {
importFrom: 'my-plugin/devtools-action',
importName: 'default',
},
})
Custom Render Entry
ctx.docks.register({
id: 'my-custom',
title: 'Custom View',
icon: 'ph:code-duotone',
type: 'custom-render',
renderer: {
importFrom: 'my-plugin/devtools-renderer',
importName: 'default',
},
})
JSON Render Entry
Build UIs entirely from server-side TypeScript — no client code needed:
const ui = ctx.createJsonRenderer({
root: 'root',
elements: {
root: {
type: 'Stack',
props: { direction: 'vertical', gap: 12 },
children: ['heading', 'info'],
},
heading: {
type: 'Text',
props: { content: 'Hello from JSON!', variant: 'heading' },
},
info: {
type: 'KeyValueTable',
props: {
entries: [
{ key: 'Version', value: '1.0.0' },
{ key: 'Status', value: 'Running' },
],
},
},
},
})
ctx.docks.register({
id: 'my-panel',
title: 'My Panel',
icon: 'ph:chart-bar-duotone',
type: 'json-render',
ui,
})
Launcher Entry
const entry = ctx.docks.register({
id: 'my-setup',
title: 'My Setup',
icon: 'ph:rocket-launch-duotone',
type: 'launcher',
launcher: {
title: 'Initialize My Plugin',
description: 'Run initial setup before using the plugin',
buttonStart: 'Start Setup',
buttonLoading: 'Setting up...',
onLaunch: async () => {
// Run initialization logic
},
},
})
Terminals & Subprocesses
Spawn and manage child processes with streaming terminal output:
const session = await ctx.terminals.startChildProcess(
{
command: 'vite',
args: ['build', '--watch'],
cwd: process.cwd(),
},
{
id: 'my-plugin:build-watcher',
title: 'Build Watcher',
icon: 'ph:terminal-duotone',
},
)
// Lifecycle controls
await session.terminate()
await session.restart()
A common pattern is combining with launcher docks — see Terminals Patterns.
Commands & Command Palette
Register executable commands discoverable via Mod+K palette:
import { defineCommand } from '@vitejs/devtools-kit'
ctx.commands.register(defineCommand({
id: 'my-plugin:clear-cache',
title: 'Clear Build Cache',
icon: 'ph:trash-duotone',
keybindings: [{ key: 'Mod+Shift+C' }],
when: 'clientType == embedded',
handler: async () => { /* ... */ },
}))
Commands support sub-commands (two-level hierarchy), conditional visibility via when clauses, and user-customizable keyboard shortcuts.
See Commands Patterns and When Clauses for full details.
Logs & Notifications
Plugins can emit structured log entries from both server and client contexts. Logs appear in the built-in Logs panel and can optionally show as toast notifications.
Fire-and-Forget
// No await needed
context.messages.add({
message: 'Plugin initialized',
level: 'info',
})
With Handle
const handle = await context.messages.add({
id: 'my-build',
message: 'Building...',
level: 'info',
status: 'loading',
})
// Update later
await handle.update({
message: 'Build complete',
level: 'success',
status: 'idle',
})
// Or dismiss
await handle.dismiss()
Key Fields
| Field | Type | Description |
|---|---|---|
message | string | Short title (required) |
level | 'info' | 'warn' | 'error' | 'success' | 'debug' | Severity (required) |
description | string | Detailed description |
| `no |
Content truncated.
When not to use it
- →Creating standalone apps without a Vite integration
- →Developing non-Vite-based devtooling
Prerequisites
Limitations
- →Restricted to the Vite DevTools lifecycle
- →Requires Vite-specific plugin architecture
- →Dependent on kit-augmented context
How it compares
It provides a standardized hook into the Vite devtools ecosystem instead of manual iframe embedding or generic plugin logic.
Compared to similar skills
writing-vite-devtools-integrations side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| writing-vite-devtools-integrations (this skill) | 1 | 2mo | No flags | Advanced |
| browser-extension-builder | 6 | 6mo | No flags | Intermediate |
| stitch-loop | 2 | 3mo | No flags | Advanced |
| e2e | 0 | 4mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
browser-extension-builder
davila7
Expert in building browser extensions that solve real problems - Chrome, Firefox, and cross-browser extensions. Covers extension architecture, manifest v3, content scripts, popup UIs, monetization strategies, and Chrome Web Store publishing. Use when: browser extension, chrome extension, firefox addon, extension, manifest v3.
stitch-loop
google-labs-code
Teaches agents to iteratively build websites using Stitch with an autonomous baton-passing loop pattern
e2e
AsiaOstrich
[UDS] 從 BDD 場景生成 E2E 測試骨架,支援框架偵測與覆蓋差距分析
vibe-check
VibiumDev
Browser automation for AI agents. Use when the user needs to navigate websites, read page content, fill forms, click elements, take screenshots, or manage browser tabs.
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
threejs-skills
sickn33
Three.js skills for creating 3D elements and interactive experiences