From Figma to React with a shadcn registry
A shadcn registry turns a Figma design system into code people install with one command. Figma variables become Tailwind v4 theme tokens, each component or block becomes a registry item with its files and dependencies, and the shadcn CLI, or an AI agent over MCP, copies them into a project where the code is theirs to edit. This is the workflow mebius.design uses for the code of every kit it sells.
Why a registry, not an npm package
- The code lands in the project’s own files. People change a component without forking a package or fighting its styles.
- Each item declares its npm dependencies and the registry items it needs, so the CLI installs exactly what a block uses.
- shadcn CLI 4 reads namespaced registries from components.json, with headers for private or paid code.
- The same items are readable by AI agents through an MCP server, so “add the pricing block” works in an editor too.
The trade-off: there is no version to bump. When the source changes, people re-run the add command and review the diff. See shadcn’s registry docs for the format.
Step 1: variables become theme tokens
One registry:theme item per kit, installed first.
Export the Figma colour variables with their light and dark modes, then write them as a theme item. cssVars.theme lands in Tailwind v4’s @theme (so bg-surface and text-fg work), and light and dark hold the values per mode:
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "acme-theme",
"type": "registry:theme",
"cssVars": {
"theme": {
"color-surface": "var(--surface)",
"color-fg": "var(--fg)",
"color-accent": "var(--accent)"
},
"light": { "surface": "#ffffff", "fg": "#0a0a0a", "accent": "#3a5eff" },
"dark": { "surface": "#0a0a0a", "fg": "#fafafa", "accent": "#6f8bff" }
}
}Name tokens by role (surface, text, border, accent), as the Figma variables do, not by colour. Text styles become utilities the same way, and a kit with one Figma mode sets the same values in light and dark.
Step 2: one Figma component, one registry item
Variants become props; nested instances become registry dependencies.
- A variant property (Size = sm / md, State = default / disabled) becomes a prop or a native state such as :disabled, never a separate component.
- Auto layout maps to flex with the same gaps and padding; Figma draws strokes inside the frame, so subtract the border from the padding to keep the outer size.
- A block that uses the kit’s button lists that button in registryDependencies instead of copying it.
- Sample content goes into defaults the block renders without props, so a fresh install shows the Figma frame.
The item for a block, here a chart card from the free charts registry, abridged:
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "charts-ethereum",
"type": "registry:block",
"title": "Ethereum Chart",
"dependencies": ["animejs"],
"registryDependencies": [
"utils",
"@mebius/charts-theme",
"@mebius/charts-chart-card",
"@mebius/charts-segmented-control"
],
"files": [
{ "path": "registry/charts/blocks/ethereum-chart.tsx", "type": "registry:component" }
],
"meta": { "figmaNodeId": "1473:82288" }
}Step 3: check the code against Figma
What makes the code worth paying for is that it matches the file.
Render every item
A small Vite playground shows each component and block with the sample data of its Figma frame.
Compare screenshots
Screenshot each demo at the frame’s size and compare it with the Figma export, state by state.
Type-check the docs
Every usage example in the docs compiles against the real source, so copied code works.
Install it for real
Build the registry and install items into a fresh Next.js app and a Vite app with the CLI.
Write down where the code differs from Figma on purpose (a missing hover state, a value that only made sense as placeholder) so designers can fix the file.
Step 4: build and serve the registry
npx shadcn@latest build turns registry.json and its items into one JSON file per item. A public registry can serve them as static files; a paid one serves them from a route that checks a token, and buyers add the header in their components.json. For the @mebius registry it looks like this:
{
"registries": {
"@mebius": {
"url": "https://www.mebius.design/r/{name}.json",
"headers": {
"Authorization": "Bearer ${MEBIUS_TOKEN}"
}
}
}
}Serve the registry from the final URL: a URL that redirects (apex to www, http to https) makes clients drop the Authorization header.
Step 5: let AI agents read it
shadcn’s own MCP server reads the registries in components.json, so an agent can search and add items from them. A registry can also run its own MCP server with richer tools (full source, usage docs, install commands). Both are covered in Install UI components with the shadcn CLI and an MCP server.
To see the result of this workflow, every kit on mebius.design with code ships its Figma file and the matching React + Tailwind registry, and the free charts and free forms come with their Figma files too.
Related
Item pages, kits and guides this guide uses.