A Vite plugin that instruments React and Vue components to provide visual highlighting and automatic Storybook story generation during development. Hover over components in your running app to see their details and create stories with a single click.
- Component Highlighting - Visual overlay on React and Vue components with configurable colors
- One-Click Story Generation - Create Storybook stories directly from your running app
- Interaction Recording - Record user interactions and generate stories with play functions
- Props Serialization - Properly serializes JSX children, Vue slots, nested components, and reactive objects
- Append to Existing Stories - Add new story variants to existing story files
- Smart Imports - Automatically resolves and adds component imports
- DevTools Integration - Built-in Vite DevTools Kit dock panel with Storybook, Coverage, Terminal, and Docs tabs
- Coverage Dashboard - Track story coverage across all detected components
- Copy Prompt - Copy LLM-friendly component context to clipboard for AI-assisted development
- Performance Optimized - Only active in development, tree-shaken in production
- Keyboard Shortcuts - Quick toggles and navigation
npm install vite-plugin-experimental-storybook-devtools
# or
pnpm add vite-plugin-experimental-storybook-devtools
# or
yarn add vite-plugin-experimental-storybook-devtoolsThis plugin requires:
vite>= 5.0.0@vitejs/devtools>= 0.1.0- One of:
react>= 18.0.0 orvue>= 3.0.0
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { DevTools } from '@vitejs/devtools'
import componentHighlighter from 'vite-plugin-experimental-storybook-devtools/react'
export default defineConfig({
plugins: [
react(),
DevTools(),
componentHighlighter(),
],
})// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { DevTools } from '@vitejs/devtools'
import componentHighlighter from 'vite-plugin-experimental-storybook-devtools/vue'
export default defineConfig({
plugins: [
vue(),
DevTools(),
componentHighlighter(),
],
})npm run devOpen Vite DevTools (floating button, usually bottom-right) and activate the Component Highlighter dock entry.
Once the dock is active:
- Hover over any component to see its highlight and tooltip
- Click on a component to open the context menu
- Press Alt/Option to toggle click-through mode (interact with the app underneath highlights)
- Create stories with a single click
| Mode | Trigger | Description |
|---|---|---|
| Hover | Mouse over | Highlights single component under cursor |
| Click-through | Press Alt/Option |
Toggles click-through mode so you can interact with the app underneath highlights |
| Clear Selection | Escape |
Clears current component selection |
| Exit Highlighting | Escape x2 (within 600ms) |
Turns off highlight mode entirely |
- Pink solid border - Currently hovered component
- Pink dashed border - Other instances of the same component type
- Pink background (20%) - Selected component (context menu open)
Click on a highlighted component to open the context menu, which provides:
Action buttons (top row):
- Open Code - Opens the component source file in your editor
- Copy Prompt - Copies an LLM-friendly prompt with component name, file path, current props, and story status to clipboard
- Open Story - Opens the story file in your editor (disabled if no story exists yet)
- View Story - Navigates to the story in the embedded Storybook panel
Properties section:
- Displays all current props with type-colored badges (strings, numbers, booleans, functions, objects, JSX/slots)
- Expandable object viewer with copy buttons
- Collapsible props section
Story creation:
- Story name input - Pre-filled with a smart suggestion based on props (variant, type, size, etc.)
- Create - Generates a story file with the current props
- Create with Interactions - Records user interactions (clicks, typing, selections) and generates a story with a play function
- Click on a highlighted component to open the context menu
- Enter a story name (auto-suggested based on meaningful props like variant, size, type)
- Click "Create" to generate a story with current props
- Or click "Create with Interactions" to record interactions first, then click the stop button to save
The story file is created at <component-dir>/<ComponentName>.stories.{ts,tsx} (.ts for Vue, .tsx for React). If the file already exists, a new named export is appended.
The DevTools panel includes a Coverage tab that shows:
- A progress bar with color-coded coverage percentage
- A table of all detected components with their story status
- Create all button - Creates stories for all visible component instances on screen, deduplicating by props fingerprint
- Per-component create buttons for individual story generation
- Visibility indicators showing which components are currently rendered
| Tab | Description |
|---|---|
| Storybook | Embedded Storybook iframe with start/status controls |
| Coverage | Component story coverage dashboard with bulk creation |
| Terminal | Live Storybook process output with error highlighting |
| Docs | Embedded Storybook documentation |
componentHighlighter({
// Glob patterns for files to instrument
include: ['**/*.{tsx,jsx}'], // React
include: ['**/*.vue'], // Vue
// Glob patterns to exclude
exclude: ['**/node_modules/**', '**/dist/**'],
// Subdirectory for generated story files (relative to component)
storiesDir: undefined,
// Enable debug logging
debugMode: false,
// Force instrumentation in production (default: false)
force: false,
})The following patterns are excluded by default:
**/node_modules/****/dist/****/*.d.ts**/*.stories.***/stories.***/*.story.***/story.*
import type { Meta, StoryObj } from '@storybook/react-vite';
import { fn } from 'storybook/test';
import MyButton from './MyButton';
import Icon from './Icon';
const meta: Meta<typeof MyButton> = {
component: MyButton,
};
export default meta;
type Story = StoryObj<typeof MyButton>;
export const Primary: Story = {
args: {
variant: 'primary',
label: 'Click me',
icon: <Icon name="star" />,
onClick: fn(),
},
};import type { Meta, StoryObj } from '@storybook/vue3-vite';
import Button from './Button.vue';
const meta: Meta<typeof Button> = {
component: Button,
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Secondary: Story = {
render: (args) => ({
components: { Button },
setup() {
const componentArgs = Object.fromEntries(
Object.entries(args).filter(([key]) => !key.startsWith('slot:')),
);
return { componentArgs };
},
template: `<Button v-bind="componentArgs">Click me</Button>`,
}),
args: {
variant: 'secondary',
size: 'default',
},
};| Type | React | Vue | Generated Code |
|---|---|---|---|
| Primitives | "hello", 42, true |
Same | Direct values |
| Objects | { nested: { value: 1 } } |
Reactive objects auto-unwrapped | { nested: { value: 1 } } |
| Arrays | [1, 2, 3] |
Same | [1, 2, 3] |
| JSX Elements | <Icon /> |
N/A | <Icon /> (with import) |
| Vue Slots | N/A | <slot /> |
Template syntax in render function |
| Functions | onClick={handler} |
@click="handler" |
fn() (with import) |
| Children | <>Hello <Button /></> |
Default slot content | Framework-specific syntax |
For detailed technical documentation, see docs/ARCHITECTURE.md.
+-----------------+ +------------------+ +-----------------+
| Vite Plugin | | Runtime Module | | DevTools Dock |
| | | | | |
| - Transform |--->| - Registration |--->| - Panel UI |
| - Inject meta | | - Registry | | - RPC Handler |
| - Endpoints | | - Serialization | | - Story create |
+-----------------+ +------------------+ +-----------------+
| |
v v
+------------------+ +---------------------+
| Client Overlay | | Framework Story Gen |
| - Highlights | | - React generator |
| - Context menu | | - Vue generator |
| - Interactions | | - Shared utilities |
+------------------+ +---------------------+
- Build-time: Framework-specific transforms inject component metadata (React via Babel AST, Vue via SFC compiler)
- Runtime: Framework wrappers (React HOC / Vue composable) register component instances with metadata, props, and DOM elements
- Interaction: Client overlay renders highlights on hover/click, shows context menu with props and actions
- Story Creation: Serialized props are sent via DevTools RPC to the server plugin, which dynamically loads the framework-specific story generator and writes story files to disk
- Interaction Recording: User actions are captured as an ordered list of steps, then formatted into a Storybook play function
| Shortcut | Action |
|---|---|
Mod+Shift+H |
Toggle component highlighter (via command palette) |
Alt/Option (press) |
Toggle click-through mode (interact with app underneath highlights) |
Escape |
Clear selection / close context menu |
Escape x2 (within 600ms) |
Exit highlight mode entirely |
Enter (in story name input) |
Create story |
git clone https://github.com/storybookjs/vite-plugin-storybook-devtools.git
cd vite-plugin-storybook-devtools
pnpm install# Run React playground
pnpm --filter playground-react dev
# Run Vue playground
pnpm --filter playground-vue dev
# Run unit tests
pnpm test
# Run E2E tests (starts playgrounds automatically)
pnpm exec playwright test
# Build the library
pnpm build
# Type check
pnpm typechecksrc/
create-component-highlighter-plugin.ts # Main Vite plugin (server endpoints, RPC, transforms)
runtime-helpers.ts # Shared runtime utilities (DOM tracking, observers)
coverage-dashboard.ts # Server-side coverage computation
notifications.ts # Notification abstraction (DevTools logs + console)
shared-types.ts # Shared types for server/client RPC transfer
frameworks/
types.ts # Shared framework interfaces
react/
plugin.ts # React entry point
index.ts # React framework config
transform.ts # Babel AST transformation
runtime-module.ts # React HOC + registration
story-generator.ts # React story generation
vue/
plugin.ts # Vue entry point
index.ts # Vue framework config
transform.ts # Vue SFC transformation
runtime-module.ts # Vue composable + registration
story-generator.ts # Vue story generation
vnode-to-template.ts # VNode to template serialization
client/
overlay.ts # Highlight UI, story file cache, save actions
context-menu.ts # Context menu (Shadow DOM), props display, actions
listeners.ts # Mouse/keyboard event handlers, highlight mode state
vite-devtools.ts # DevTools dock lifecycle (activate/deactivate)
interaction-recorder.ts # User interaction recording for play functions
coverage-actions.ts # Client-side coverage actions (scroll, highlight)
logger.ts # Debug logging utility
utils/
format-utils.ts # Value formatting helpers for context menu
html-preview.ts # HTML preview rendering for prop values
prop-utils.ts # Prop type detection and badge utilities
codegen/
interactions-to-code.ts # Converts recorded interactions to play function code
generate-query.ts # Generates Testing Library queries from targets
args-to-string.ts # Serializes args objects to source code strings
combine-interactions.ts # Combines/deduplicates sequential interaction steps
get-interaction-event.ts # Maps DOM events to interaction event types
types.ts # Codegen type definitions
panel/
panel.ts # DevTools panel (Storybook, Coverage, Terminal, Docs tabs)
panel.css # Panel styles
index.html # Panel HTML shell
utils/
story-generator.ts # Shared story generation utilities
e2e/
highlighter-helpers.ts # Shared E2E helper functions
common-highlighter-suite.ts # Shared test suite (both frameworks)
component-highlighter.spec.ts # Highlighter interaction tests
playground-react-detection.spec.ts # React-specific detection tests
playground-vue-detection.spec.ts # Vue-specific detection tests
playground/
react/ # React development app
vue/ # Vue development app
- React & Vue only - Currently supports React and Vue (other frameworks planned)
- Development only - Disabled in production builds by default
- Vite DevTools required - Needs
@vitejs/devtoolsfor the dock panel and RPC - Function components - Class components are not supported
- Provider dependencies - Components requiring context providers may need Storybook decorators
- Ensure the DevTools dock is open and the Component Highlighter entry is active
- Check the browser console for errors
- Verify the output path is writable
- Ensure the file matches the
includepatterns - Check that it's not matching an
excludepattern - Verify the component is a function component (class components are not supported)
- For Vue, ensure the component has a
<script setup>or<script>block
- Check that component references are in the live registry (rendered on screen)
- Vue components need the
.vueextension in the import path
MIT