Spread polish across your React prototype.
A Claude Code plugin that turns any JSX prototype into a fleet of deck-ready demo screens — real interactive HTML on top of AI-generated ambient backdrops.
You point Better UI at a React prototype HTML file. It walks the prototype, drafts a config from your own theme tokens, and produces:
- A polished per-screen page for every screen in your prototype — real interactive HTML, your JSX still mounted, your theme intact.
- An AI-generated "mood-board" mockup for each screen —
gpt-image-2polishes typography, spacing, and editorial feel. - An AI-generated ambient backdrop plate stripped of all UI — wallpaper for the live page to sit on top of.
- Deck-ready final captures — the live HTML composited over its backdrop, ready to drop into slides.
The real UI is never raster. The AI image is never used for affordances. You get something that looks like a polished mockup but behaves like a prototype, with thumbnails, navigator, and per-screen pages on disk.
your prototype.html
│
▼
┌─────────────────┐
│ extract JSX + │ ◀── Claude reads the bundle, infers screens,
│ theme tokens │ props, and style direction from your code
└────────┬────────┘
▼
┌─────────────────┐
│ build pages │ ◀── one HTML per screen, your JSX inlined,
│ /flow/step.html│ theme.css + components.css linked
└────────┬────────┘
▼
┌─────────────────┐
│ capture raw │ ◀── Playwright @1536×1024, no backdrop
│ reference/raw/ │
└────────┬────────┘
▼
┌─────────────────┐
│ improve pass │ ◀── gpt-image-2: polished editorial mockup
│ reference/ │ (cost-gated — confirm before spending)
│ improved/ │
└────────┬────────┘
▼
┌─────────────────┐
│ strip-UI pass │ ◀── gpt-image-2: ambient backdrop plate
│ reference/ │ (cost-gated)
│ background/ │
└────────┬────────┘
▼
┌─────────────────┐
│ final capture │ ◀── live UI composited on the plate
│ reference/final/│
└────────┬────────┘
▼
pages/index.html ◀── thumbnail navigator
From the parent directory of this repo:
claude --plugin-dir ./BetterUIThen inside Claude Code, the 12 slash commands appear under /better-ui:*. Run /help to verify.
Note
Better UI works on any prototype that decodes via the inline-manifest pattern (<script type="__bundler/manifest">) or external-scripts pattern (standard <script type="text/babel" src="…">). For unrecognized patterns, drop your JSX into .better-ui/extracted/ manually and run /better-ui:configure.
/better-ui:setup
You'll be asked for your OpenAI API key — it goes into your project's .env (gitignored automatically). Or store it globally at ~/.claude/better-ui.env for reuse across projects.
/better-ui:bootstrap ./MY-PROTO.html
Sit back. The pipeline runs end-to-end with cost gates before every AI pass. Typical 30-screen prototype: ~30 min, ~$2.30 USD at medium quality.
| Command | Stage | What it does |
|---|---|---|
/better-ui:setup |
– | Validate / prompt for the OpenAI key. Install Playwright. Idempotent. |
/better-ui:bootstrap [<proto>] |
full | End-to-end pipeline with cost gates between AI stages. |
/better-ui:extract <proto> |
1 | Decode the prototype bundle into .better-ui/extracted/. |
/better-ui:configure |
2a | Have Claude draft better-ui.config.json from the extracted JSX. |
/better-ui:build |
2b | Regenerate pages/{flow}/{step}.html from config + JSX. |
/better-ui:capture |
4 | Playwright raw screenshots → reference/raw/. |
/better-ui:improve [--only=…] |
5 | gpt-image-2 polish pass (cost-gated). |
/better-ui:plates [--only=…] |
6 | gpt-image-2 strip-UI pass (cost-gated). |
/better-ui:index |
7 | Build pages/index.html thumbnail navigator. |
/better-ui:final |
8 | Deck-ready captures with backdrops composited. |
/better-ui:new-screen <flow> <step> |
– | Append a screen + stub component. |
/better-ui:verify |
– | Structural consistency check (CSS links, backdrop divs, no improved-as-img). |
After bootstrap runs in your project:
your-project/
├── better-ui.config.json ← source of truth, edit freely
├── .env ← OPENAI_API_KEY (auto-gitignored)
├── .better-ui/
│ ├── extracted/ ← decoded JSX (reference only)
│ └── prompts/ ← editable per-project AI prompts
├── pages/
│ ├── index.html ← thumbnail navigator
│ ├── _shared/
│ │ ├── theme.css ← lifted from your prototype
│ │ ├── components.css ← top menu + backdrop layer
│ │ ├── jsx/ ← editable JSX (source of truth)
│ │ └── vendor/ ← react, react-dom, babel-standalone
│ └── {flow}/{step}.html ← generated; never hand-edit
└── reference/
├── raw/{flow}/{step}.png ← Playwright shots, no backdrop
├── improved/{flow}/{step}.png ← gpt-image-2 mood-board mockups
├── background/{flow}/{step}.png ← gpt-image-2 ambient plates
└── final/{flow}/{step}.png ← live UI on plate, deck-ready
| You want to change… | Run |
|---|---|
| A screen's behavior | edit pages/_shared/jsx/<flow>.jsx → auto-rebuilds via hook → refresh |
| The top menu for a flow | edit flows.<flow>.topMenu in better-ui.config.json → /better-ui:build |
| The visual theme | edit pages/_shared/theme.css → just refresh |
| One screen's AI mockup | /better-ui:improve --only=<flow>/<step> --force |
| One screen's backdrop | /better-ui:plates --only=<flow>/<step> --force |
| The improve prompt | edit config.style.preamble → re-run improve |
| Thumbnails on the index | /better-ui:index |
| Deck re-shoot | /better-ui:final |
gpt-image-2 at medium / 1536×1024:
| Item | Approx |
|---|---|
| Per image | ~$0.04 USD |
| Per image wall-time | ~45 s + 13 s pacing |
| 29 screens × 2 passes | ~75 min, ~$2.30 USD |
Important
Every AI-emitting skill (improve, plates, bootstrap) shows you the exact count, dollar estimate, and wall-time before the API call. You can always pick a subset.
Every script reads better-ui.config.json. Edit it freely — it's the only file you need to change to retarget the pipeline.
Example config (click to expand)
The style.preamble field controls how gpt-image-2 interprets your raw screenshots. bu-configurer (the configurer subagent) writes a starting preamble derived from your prototype's own theme tokens — tighten it with project-specific language to get more coherent output across all screens.
This is the part most plugins skip. Better UI uses Claude itself at bootstrap time:
extract-bundle.mjsdecodes the HTML into editable JSX in.better-ui/extracted/.- The
bu-configurersubagent reads every.jsxfile and the prototype's<style>block. - It detects flow modules, screen components, prop signatures, and theme tokens — then writes
better-ui.config.jsonwith a style preamble grounded in your design language. - You review the config (and edit it if anything looks off) before any AI call.
That's how the same plugin works on a Sotheby's-inspired auction site, a SaaS dashboard, a fintech app, or a media reader — without a single hardcoded screen name or style word.
- Mobile / responsive layouts (demo is 1536×1024, desktop-only)
- Non-React frameworks (Vue, Svelte) — though contributions welcome
- Image asset versioning (use git to snapshot
reference/before re-runs) - Visual diff between improved and final captures
- CDN-served vendor scripts (everything is local for airgap compatibility)
- Marketplace publishing (use
--plugin-dirfor now)
MIT © Eliseo Robles

{ "$schema": "https://better-ui.dev/schema/v1.json", "schemaVersion": 1, "projectName": "My Demo", "prototype": "./MY-PROTO.html", "viewport": { "width": 1536, "height": 1024, "deviceScaleFactor": 2 }, "image": { "model": "gpt-image-2", "size": "1536x1024", "quality": "medium", "delayMs": 13000, "estimatedCostPerImageUsd": 0.04 }, "server": { "port": 8765, "host": "127.0.0.1" }, "backdrop": { "opacity": 0.55, "saturation": 0.85 }, "style": { "preamble": "Premium editorial UI mockup using <your theme tokens>. Preserve composition. Refine typography…", "stripPrompt": null }, "flows": { "buyer": { "label": "Buyer", "jsxModule": "pages/_shared/jsx/buyer.jsx", "topMenu": ["Home", "Browse", "Activity", "Account"], "screens": [ { "step": "today", "component": "BuyerToday", "props": "go={ctx.go} setActiveDrop={ctx.setActiveDrop}", "label": "Today", "hint": "Emphasize the daily streak tile and the upcoming drops list." } ] } }, "consistency": { "requirePageShell": true, "forbidImprovedAsImg": true } }