You bring the component
Real props, real styles, real state. windo renders it verbatim — no wrapper format, no story DSL to translate it into.
A component workbench you can read in one sitting. Point Vite at your *.windo.tsx files and get an isolated canvas with live props, variants, and ambient contexts — no separate build, no story boilerplate, no config to babysit.
Isolating a component to develop it in is a good instinct. The tools that do it tend to charge for it up front:
None of that is the component. It's scaffolding around the component, and it compounds as the catalogue grows.
windo doesn't bring a build. It rides the Vite build you already have. The plugin pairs @vitejs/plugin-react with windo, discovers every file matching your include glob, and serves the workbench — chrome on the outside, an isolated preview iframe on the inside.
There is exactly one configuration file. windo.config.ts declares your sidebar groups, optional cross-cutting tags (labels the sidebar filters by), and any ambient contexts, and hands back a windo factory you import into each preview file. No addon registry, no preset layer, no second toolchain.
import { defineConfig } from 'vite' import { windo } from '@westopp/windo/plugin' export default defineConfig({ plugins: [windo({ config: 'windo.config.ts' })], })
A preview is a *.windo.tsx file that default-exports the result of the windo factory. The factory describes the component once — its title, where it lives, its editable props — and renders it. That description is the same prop type your component already uses, narrowed by a zod schema, not a parallel format you maintain by hand.
windo is built around the same principle as the rest of the library family — Bring Your Own Components. You write the component; windo never owns its markup, its styles, or its state shape. What windo supplies is everything around it: the store that holds live prop edits, the preview surface that mounts it, and the controls that drive it.
The split is clean. Your component receives ordinary props and renders. windo reads your zod schema, turns its input shape into the JSON editor and Schema panel, validates each edit through the live schema — transforms, coerce, refine and all — then hands the parsed output to your component. Anything the JSON omits falls back to the defaultProps you declared. The component never learns it is being previewed.
Real props, real styles, real state. windo renders it verbatim — no wrapper format, no story DSL to translate it into.
Your zod schema becomes the prop editor and Schema panel. Edits are validated, then parsed; invalid input surfaces per-field errors instead of crashing the preview.
Each component mounts in a real iframe. Its CSS, layout, and media queries are sealed off from the workbench chrome around it.
A component is only honestly isolated if nothing leaks across the boundary. windo renders each preview inside a real iframe, not a styled div. That buys real isolation: the workbench's own CSS can't bleed into your component, your component's global styles and resets can't bleed into the chrome, and media queries resolve against the iframe's box — so resizing the canvas exercises your responsive breakpoints exactly as a viewport would.
The chrome and the iframe talk over a typed message protocol. The chrome pushes ambient environment down — color scheme, viewport, reduced motion, direction, locale, and any context control values — and the preview reports back logs and validation errors. You develop the component in conditions close to where it ships, without the workbench distorting them.
windo is for previewing and developing components in isolation, fast, with a setup small enough to read. It fits when:
state plus actions that fire from toolbar buttons or stage pointer events — to drive interactive states like loading, hover, or open.It is deliberately not a few things. It is not a test runner — there are no assertions, no snapshots, no CI mode; it's a development surface. It is not a published docs site — the chrome is a workbench, not a site generator, and these docs are built separately. And it is not bundler-agnostic — windo is a Vite plugin, so a non-Vite app is outside its lane. If any of those is what you need, reach for the tool built for it; windo stays focused on the canvas.
Follow installation to add the package and wire the plugin, then getting started to put your first component on the canvas. For the boundary in detail — how the iframe, the schema, and the message protocol fit together — read how it works.