Every release cycle at Medius, engineers sat down with a Figma file and hand-translated it into React — component by component, prop by prop — until it matched the 350+ entry design system our product teams were required to use. It wasn't hard work, exactly. It was worse: it was repetitive, low-skill, and impossible to skip, because a screen that doesn't use MediusButton or MediusDataGrid correctly doesn't ship. Multiply that across a dozen teams and it becomes a full-time tax on the org, paid every sprint.
The obvious fix — throw an AI design-to-code tool at it — turned out not to be obvious at all. We tried the field's default answers before building our own, and all of them failed the same way.
Why the market's tools don't know what your components look like
Figma's own AI came in under 30% accuracy in our internal testing. V0 and Bolt.new produce clean-looking React, but it's generic React — divs, Tailwind, maybe shadcn — with no concept that Medius UI exists at all. Figma's Code Connect gets closer, since it's built to map designs to real components, but it has no mechanism for enforcing a catalogue: it happily emits props that don't exist, layouts that don't respect our spacing rules, or components that look right in the screenshot but are wrong in the codebase. Every one of these tools treats "generate some JSX" and "generate JSX my design system will accept" as the same problem. They aren't.
That gap is where the manual refactoring hides. A generated screen that's 90% right but references three nonexistent props isn't a time-saver — it's a debugging exercise with extra steps.
Why we didn't fine-tune, and why RAG wasn't enough either
The instinctive next move is to fine-tune a model on Medius UI usage, or to retrieve relevant Storybook docs into the prompt and let GPT-4o figure out the rest. We ruled out both, for related reasons.
Fine-tuning needed a labeled dataset of Figma-to-Medius-JSX pairs that didn't exist and would have been expensive to build — and worse, it would go stale the moment the design system did. A component library at a growing company changes weekly; nobody wants to retrain a model every sprint. Plain retrieval-augmented generation was closer, but it only informs the model — it pulls relevant docs into context and hopes the completion respects them. Hope isn't a constraint. Retrieval still let GPT-4o improvise a prop that sounded plausible and wasn't real.
What we needed wasn't a model that knew more about Medius UI. It was a model that was not allowed to generate anything outside it.
The smallest ground-truth structure the model has to stay inside
So instead of expanding what the model knew, we shrank what it was allowed to say. We built a component catalogue directly from the Medius Storybook documentation — the one source of truth that was already accurate, because engineers kept it accurate as a side effect of their normal jobs.
Each entry carried exactly what the model needed and nothing it could misuse:
catalogue_entries = [
{
"id": e["id"],
"code": e["code"],
"props": e.get("props", {}),
}
for e in storybook_entries
if e.get("code") and e.get("screenshot")
]id, a real JSX usage snippet, and the exact valid props with their types and defaults. That catalogue did double duty: it was injected into the generation prompt to restrict what GPT-4o could produce, and it was run again as a post-generation check to validate what it actually did produce. Generation and verification shared the same ground truth, so there was no gap for hallucinated props to slip through.
That's the actual insight behind this project, and it generalizes past Medius: the win was never going to come from a smarter model. GPT-4o was already smart enough. The win came from building the smallest structure that the model has to stay inside — small enough to keep accurate by hand, precise enough that "correct" and "in the catalogue" become the same statement.
The seven-stage pipeline
Around that catalogue, we built an end-to-end path from a Figma selection to rendered, testable code:
- Figma extraction — pull the node's image and structural metadata from a shared link.
- Component matching — map extracted elements against the catalogue to find the real components involved.
- Constrained generation — GPT-4o (Azure OpenAI) produces JSX using only catalogue components and their real props.
- Live render, behind an error boundary — the output renders immediately, so failures are caught before they reach a developer.
- Reflexion loop — render errors get fed back to the model as correction context, so most syntax and prop mistakes get fixed without a human in the loop.
- Chat interface — designers and developers can say "make the button smaller" in plain English and get an updated component, not a diff to interpret.
- Structured logging — every generation, correction, and manual edit is written to JSON, capturing the before/after code and the user's stated rationale, for analysis and future refinement.
The reflexion loop and the error-boundary render turned out to matter almost as much as the catalogue itself — hard constraints stop hallucination, but they don't stop typos, and a pipeline that dies on the first undefined is not a function isn't usable by non-technical stakeholders.
Built the way it had to be used
I worked this end-to-end in an XP-style pairing arrangement with the Medius platform architect — they owned the architectural calls, I owned the implementation — which meant most non-trivial decisions got reviewed the same day they were made rather than at the end of a sprint. Work itself ran on Kanban: no fixed iteration length, a hard cap on work-in-progress, and priorities that could shift when the catalogue itself changed underneath us, which it did, regularly.
What shipped
The system hit 90–95% visual fidelity against the original Figma designs, held strict adherence to Medius UI's component and prop constraints, and recovered automatically from the common failure classes that used to require a developer's attention. Against every alternative we evaluated — Figma's AI, V0, Bolt.new, fine-tuning — it was the only approach that scored well on automation, component awareness, reusability, accuracy, and design-system support simultaneously, because it was the only one built around a hard constraint instead of a better guess.
The win wasn't a better model — it was building the smallest ground-truth structure the model has to stay inside.
If there's a lesson worth carrying to the next design-system-constrained generation problem, that's it. Don't ask the model to know your rules. Build the rules into the space it's allowed to generate in.