windo/Why windo
Getting started~3 min

Why windo

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.

What a workbench usually costs

Isolating a component to develop it in is a good instinct. The tools that do it tend to charge for it up front:

  • A parallel build. A second bundler, its own config, its own dependency tree to keep in step with the app's. When they drift, you debug the workbench instead of the component.
  • Story boilerplate. A file format with its own conventions — args, decorators, parameters — that you learn and maintain alongside your actual props. The component already declares its shape; now you describe it twice.
  • A config surface that grows. Addons, framework presets, webpack overrides. The setup becomes a project of its own, separate from the thing you set out to preview.

None of that is the component. It's scaffolding around the component, and it compounds as the catalogue grows.

windo's answer: one config, your bundler

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.

TSvite.config.ts
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.

BYOC: you bring the components

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.

01

You bring the component

Real props, real styles, real state. windo renders it verbatim — no wrapper format, no story DSL to translate it into.

02

windo brings the controls

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.

03

The iframe brings isolation

Each component mounts in a real iframe. Its CSS, layout, and media queries are sealed off from the workbench chrome around it.

Why an iframe

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.

When windo fits

windo is for previewing and developing components in isolation, fast, with a setup small enough to read. It fits when:

  • You already build with Vite and want the workbench to share that toolchain rather than stand up a second one.
  • Your components take serialisable-ish props you can describe with a zod schema — the editable surface is JSON, validated by that schema.
  • You want live controls, variants, and ambient contexts (theme, locale, direction) without writing addon code to get them.
  • You want a per-preview state machine — a typed 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.

Where next

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.