New package · React 18+

UI variants,
switched in-app.

React library for in-app prototype variant switching. URL-persisted. Zero runtime deps. Works with or without the Vibeflow overlay.

Open live demo Install — it's free Read the docs on GitHub

npm install @vibeflow-tools/prototyping  ·  Requires React 18+

// why it matters

Switch variants without
touching your code.

Stop rebuilding to review UI states. Bring the switch directly into your running app.

URL-PERSISTED
Share a link, share a state
Active variants are encoded in the URL as ?vf-<name>=<variant>. Share a URL and your reviewer sees the exact variant you're showing them — no screenshots, no guesswork.
TYPESCRIPT-FIRST
Full autocomplete, zero any
Variant keys are narrowed to their literal types. Your IDE knows every property on the active config object. Refactor variant names with confidence — the compiler has your back.
ZERO RUNTIME DEPS
Nothing extra in your bundle
Peer-deps only — React and React DOM. No extra kilobytes, no transitive surprises. The entire library adds under 5 kB to your bundle, tree-shaken.
VIBEFLOW INTEGRATION
Right-click access via the overlay
When the Vibeflow overlay is detected on the page, the toolbar button hides itself and registers with the overlay. Access variant controls via the right-click context menu under "Prototyping".
// how it works

Three steps.
Running in minutes.

Provider → hook → switcher. That's the whole model.

1Wrap
Provide the context.
Add VariantProvider once at your app root (or any subtree). All hooks and switcher components beneath it share state through this provider.
// App.tsx
import { VariantProvider } from '@vibeflow-tools/prototyping'
<VariantProvider>
  <App />
</VariantProvider>
2Register
Define your variants.
Call useVariant(name, variants) in any component. Pass a map of variant configs — the first key is always the default. The hook resolves URL → localStorage → default.
// HeroSection.tsx
const variants = {
  default: {},
  compact: { spacing: 'tight' },
  expanded: { spacing: 'loose' },
}
const v = useVariant('Hero', variants)
// v.spacing is typed as 'tight' | 'loose' | undefined
3Switch
Pick your UI control.
Add a switcher component next to your element. Use <VariantSwitcher> for a dot on the element edge, <PageVariantSwitcher> for a fixed segmented control, or <VariantDevToolbar> for a floating panel showing all scopes.
// dot on element edge
<VariantSwitcher name="Hero" variants={variants} />
// or page-level segmented control
<PageVariantSwitcher name="Hero" variants={variants} />
// or central toolbar for all scopes
<VariantDevToolbar />
// quick start

Full example in
20 lines.

Provider, hook, and switcher — all three in one component tree.

import { VariantProvider, useVariant, PageVariantSwitcher } from '@vibeflow-tools/prototyping'
 
const heroVariants = {
  default: {},
  compact: { spacing: 'tight', titleSize: 'md' },
  expanded: { spacing: 'loose', titleSize: 'xl' },
}
 
function HeroSection() {
  const v = useVariant('Hero', heroVariants)
  return (
    <section className={v.spacing === 'tight' ? 'py-4' : 'py-12'}>
      <PageVariantSwitcher name="Hero" variants={heroVariants} />
      <h1 className={v.titleSize === 'xl' ? 'text-4xl' : 'text-2xl'}>Welcome</h1>
    </section>
  )
}
 
function App() {
  return (
    <VariantProvider>
      <HeroSection />
    </VariantProvider>
  )
}
// api reference

Six exports.
Everything you need.

Every export is fully typed. Tree-shake what you don't use.

<VariantProvider>
Context root
Wrap your app or subtree once. All hooks and switchers below it share state.

Props:
mode"dev" (default) hides switcher UI in production · "always" always shows
defaultVisible — initial UI visibility, persisted in localStorage
shortcuts — custom keyboard shortcuts or false to disable
useVariant(name, variants)
Core hook
Registers the scope and resolves the active variant. Resolution order: URL param → localStorage → default (first key).

Returns the matching config object typed as the union of all variant objects — full IDE autocomplete included.
<VariantSwitcher>
Element-level dot
A subtle indicator dot on the edge of the parent element. Clicking expands a numbered-dot picker. Parent must have position: relative.

Deduplicates per scope — only the first instance renders even if the component appears multiple times.
<PageVariantSwitcher>
Fixed segmented control
A dark segmented control fixed to the top-left of the viewport. Ideal for page-level layout or theme switching where you want persistent visibility.

Props: name, variants — same values you pass to useVariant.
<VariantDevToolbar>
Floating central panel
A floating toolbar showing all registered variant scopes in one panel. Add once near the root inside VariantProvider.

When the Vibeflow overlay is detected, the icon button hides — access via right-click → "Prototyping" instead.
registerVariant(name, variants)
Module-level registration
Centralise all variant definitions in one file. Components call useVariant('Name', {}) and pick up the registered config automatically. Inline variants always win when both are provided.
// persistence

URL first.
localStorage fallback.

Two storage layers. URL always takes precedence, letting you share exact states with a link.

URL parameter
?vf-<name>=<variant>
e.g. ?vf-Hero=compact
Survives page reloads · Shareable by copy-pasting the URL · Reviewer sees exactly what you see
HIGHEST PRIORITY
localStorage
vf-<name>
e.g. vf-Hero = "compact"
Session memory · Survives reloads in the same browser · Cleared when you switch in-app
FALLBACK — OVERRIDDEN BY URL

Both layers are managed automatically. For manual control, import utilities: readVariantFromUrl, writeVariantToUrl, resolveActiveVariant and more.

// keyboard shortcuts

Control without
leaving your keyboard.

Two shortcuts, both call toggleUiVisible() on the provider.

Alt + H Toggle all variant switchers on/off
Ctrl + Shift + V Toggle all variant switchers on/off
Escape Close the VariantDevToolbar panel

Customise shortcuts via the shortcuts prop on <VariantProvider>, or pass shortcuts={false} to disable all.

// vibeflow integration

Better together
with the overlay.

ui-prototyping runs standalone, but unlocks an extra layer when the Vibeflow overlay is present.

Automatic overlay detection
When the Vibeflow overlay is injected into the page, <VariantDevToolbar> automatically hides its standalone icon button and registers itself with the overlay's context menu. Access variant controls via right-click → Prototyping — no extra configuration required. Detection is fully automatic.
// live demo

See it in action.

Four variant scopes — Layout, Hero, TaskCard, and Nav — with inline switchers and URL persistence.

Open live demo →
// install

Add to your project.
Ship variant reviews.

No sign-up. No backend. No API keys. Just install and prototype.

npm
npm install @vibeflow-tools/prototyping
pnpm
pnpm add @vibeflow-tools/prototyping

Requires React 18+ · Node.js 18+ · TypeScript support included · Apache 2.0