Skip to content
owerczuk.dev
Back to Blog
Figma
Design System
React
Tailwind
Frontend

Figma to Code: The Free Migration Workflow That Actually Works

A step-by-step guide to migrating Figma designs to production-ready React code using design tokens, Storybook, Tailwind, and the Figma MCP server. No paid tools required.

March 7, 202612 min

You have a polished Figma file. Every color token is named. Every spacing value earns its place. The typography scale actually makes sense.

Then you open the codebase.

#3B4FE0 hardcoded in eleven different places. Three Button implementations that look subtly different depending on which screen you're on. A styles.css so deep that nobody has scrolled to the bottom since 2023.

This is the gap between design and code — and it's quietly one of the most expensive problems in frontend work. The default solution teams reach for is a paid tool: Builder.io, Zeplin, Anima, some AI-powered SaaS that promises to collapse the distance. Those tools have their use cases. But if you're the developer who owns both the Figma file and the codebase, you can solve this yourself, locally, without a subscription, using tools that have matured significantly in the past year.

This is the workflow I actually use. Let me walk you through it.


Why most Figma migrations break down

The pattern is predictable. A designer ships a Figma file. The developer opens Dev Mode, reads some specs, and starts writing components by hand. It ships. Then the design changes in three places. Now you're hunting down everywhere that color was hardcoded. The Figma file and the code drift apart, slowly at first and then all at once.

The deeper issue isn't the tooling — it's the starting point. Most migrations treat Figma as a reference image: something to look at while you build. The moment you start implementing screens before establishing the underlying system, you've already lost.

The fix: build the design system first. Build screens on top of it.

Tokens before components. Components before pages. A living Storybook before a single feature ships. In that order, the whole problem changes character.

The migration sequence that works

StepWhatWhy
1Export design tokensSingle source of truth for all visual values
2Set up StorybookDefine component contracts before implementation
3Connect Figma MCPLet AI read your actual design, not screenshots
4Build primitivesTokens + MCP = consistent, documented components
5Compose patternsCombine primitives into reusable UI blocks
6Assemble screensAssembly, not design — AI knows your full inventory

Most teams jump straight to step 6. That's why their migrations fail.


Step 1: Export your design tokens from Figma

Design tokens are the atoms of your system — named values for colors, spacing, border radii, shadows, typography. If your Figma file uses Variables (Figma's built-in token system, available in all paid plans), you can export them directly.

The tool: Tokens Studio (free tier)

Tokens Studio for Figma reads your Figma Variables and exports them as JSON in the W3C Design Token format. This format is an open standard — Style Dictionary, Tailwind, and most modern component systems understand it natively.

The output looks like this:

{
  "color": {
    "primary": { "$type": "color", "$value": "#3B82F6" },
    "primary-hover": { "$type": "color", "$value": "#2563EB" },
    "text-base": { "$type": "color", "$value": "#111827" }
  },
  "spacing": {
    "4": { "$type": "dimension", "$value": "1rem" },
    "6": { "$type": "dimension", "$value": "1.5rem" }
  }
}

Nothing proprietary. Nothing that locks you into a platform.

Transform to Tailwind with Style Dictionary

Style Dictionary (open source, Amazon) takes that JSON and produces whatever output format you need. For Tailwind:

// style-dictionary.config.js
module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'src/styles/',
      files: [{ destination: 'tokens.css', format: 'css/variables' }]
    },
    tailwind: {
      transformGroup: 'js',
      buildPath: 'src/',
      files: [{ destination: 'tailwind.tokens.js', format: 'javascript/es6' }]
    }
  }
}

Run style-dictionary build and you get a tokens.css with CSS custom properties and a tailwind.tokens.js you extend your config with. From this point forward, your entire color palette and spacing scale are driven by a single file that traces directly back to Figma.

Automate the token pipeline

Add a GitHub Action that runs Style Dictionary whenever the tokens JSON changes. A designer updates a color in Figma, exports the JSON, and a PR opens automatically. You review, merge, and every component in the codebase updates without anyone touching a hex value.

Figma Variables
     |
     v
Tokens Studio (export JSON)
     |
     v
Git commit (tokens/*.json)
     |
     v
GitHub Action (Style Dictionary build)
     |
     v
CSS variables + Tailwind config
     |
     v
Every component updates automatically

Step 2: Set up Storybook before you write a single component

This is the piece most developers skip, and it costs them later.

The common approach is to build 20 components and then add Storybook as documentation afterwards. By then, component APIs are inconsistent, there's no standard for variants, and Storybook becomes something that looks nice in a demo but nobody actually maintains.

Starting with Storybook forces you to define your component contract before you implement it. Write the story first:

npx storybook@latest init

Then, before touching the Button implementation:

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'

const meta = {
  title: 'Primitives/Button',
  component: Button,
  argTypes: {
    variant: { control: 'select', options: ['primary', 'secondary', 'ghost'] },
    size: { control: 'select', options: ['sm', 'md', 'lg'] },
    disabled: { control: 'boolean' }
  }
} satisfies Meta<typeof Button>

export default meta
type Story = StoryObj<typeof meta>

export const Primary: Story = {
  args: { variant: 'primary', children: 'Get started' }
}

export const Ghost: Story = {
  args: { variant: 'ghost', children: 'Learn more' }
}

Writing this first tells you exactly what props you need. The story becomes the spec. Then you implement the component to pass it — not the other way around.

Story-first vs code-first: what changes

ApproachComponent APIDocumentationLong-term maintenance
Code-first, stories laterInconsistent — shaped by whatever the first screen neededIncomplete — written after the fact, often outdatedHigh — APIs diverge across components
Story-first, then implementConsistent — contract defined up frontAlways current — stories are the specLow — single source of truth for behavior

Step 3: Connect the Figma MCP server

This is where the workflow gets genuinely fast. Figma launched their official Dev Mode MCP server in mid-2025. It connects your Figma files directly to AI-powered coding environments — Cursor, Claude Code, VS Code Copilot — so your assistant can read the actual design instead of relying on screenshots or your description of it.

Setup is straightforward with the Figma desktop app installed:

// .cursor/mcp.json (or equivalent for your editor)
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "@figma/mcp-server"]
    }
  }
}

Restart the MCP client, select a component in Figma, and prompt your assistant to implement it. It calls get_design_context and gets back a structured React + Tailwind representation of your selection — including actual token names, spacing values, and typography specs.

Code Connect: the piece that makes it scale

The tool that makes this work properly at scale is Code Connect. It maps each Figma component to its code counterpart, so when the MCP server sees a "Button/Primary" in a layout, it uses your <Button variant="primary" /> instead of generating a new button from scratch. Without Code Connect, the AI is guessing. With it, it knows your entire component inventory.

Connecting a component takes about five minutes:

// Button.figma.tsx
import figma from '@figma/code-connect'
import { Button } from './Button'

figma.connect(Button, 'https://figma.com/design/YOUR_FILE_ID/...', {
  props: {
    variant: figma.enum('Variant', {
      Primary: 'primary',
      Secondary: 'secondary',
      Ghost: 'ghost'
    }),
    children: figma.string('Label')
  },
  example: (props) => <Button {...props} />
})

Do this for each primitive as you build it. By the time you start implementing screens, the MCP server knows your full component library and will use it consistently.

MCP vs traditional design-to-code approaches

MethodInput qualityUses your components?Understands tokens?Speed
Manual from Figma Dev ModeHigh (you read specs)Only if you rememberOnly if you checkSlow
Screenshot + AI promptLow (pixel guessing)NoNoFast but inaccurate
Figma MCP (no Code Connect)High (structured data)No — generates new onesYesFast
Figma MCP + Code ConnectHigh (structured data)Yes — reuses your libraryYesFast and accurate

Step 4: Build primitives in order

With tokens wired into Tailwind, Storybook running, and the MCP server connected, building primitives becomes a loop:

  1. Select a component in Figma
  2. Prompt: "Get design context for this component. Implement it using our token config — use token names, not raw Tailwind classes. Create a Storybook story covering all variants."
  3. Review the output
  4. Adjust if needed, commit

What previously took 2-3 hours per component is now a 20-minute review. The output is already aligned with your Figma design, uses your token names (not bg-blue-500), and includes a documented story.

Build in dependency order. Later components depend on earlier ones.

PhaseComponentsDependencies
FoundationTypography, Icon, BadgeTokens only
InputsButton, Input, Checkbox, Select, ToggleFoundation
FeedbackAlert, Toast, Tooltip, ProgressFoundation
LayoutCard, Modal, Dropdown, TabsFoundation + Inputs
CompositionFormField, DataTable, NavigationAll above

Structure your directory so the hierarchy is explicit from the start:

src/
  components/
    primitives/   <- Button, Input, Badge, Icon
    patterns/     <- Card, Modal, FormField, Dropdown
    layouts/      <- PageShell, Sidebar, Header
  tokens/
    tokens.json   <- Source of truth from Figma
    tailwind.tokens.js

Primitives have no business logic. Patterns compose primitives. Layouts arrange patterns. When a new project starts, you pull from this library. The work compounds — not just in time saved, but in consistency that your future self will appreciate.


Step 5: Use your system as the foundation for every new project

This is the actual payoff.

Once your primitives are documented in Storybook and driven by tokens, starting a new project means customizing a proven foundation rather than building from zero.

packages/
  ui/             <- Your design system package
apps/
  web/            <- imports @yourorg/ui
  dashboard/      <- imports @yourorg/ui
  mobile-web/     <- imports @yourorg/ui

A bug fix in Button propagates across all apps. A brand color update in Figma flows through tokens to every component. One source of truth, multiple consumers.

For freelancers or agency work: template repo

Keep a design-system starter that includes the full token pipeline, Storybook config, and a set of primitive components. When a new client project starts:

npx degit yourname/design-system new-client-project

Update the tokens JSON with the client's brand values, run Style Dictionary, and your entire Tailwind config is updated. Pixel-perfect foundation in under an hour. No SaaS, no subscriptions, everything local.


The complete toolchain at a glance

People assume this workflow requires paid tools. Here's the honest breakdown:

ToolPurposeCostLicense
Tokens StudioExport Figma Variables as JSONFree tier-
Style DictionaryTransform tokens to CSS/TailwindFreeMIT
StorybookComponent documentation and testingFreeMIT
Figma MCP ServerDesign-to-code bridge for AI editorsFreeRequires Figma account
Code ConnectMap Figma components to codeFreeRequires Dev seat
ChromaticVisual regression testingFree (5k snapshots/month)-

The only real cost is a Figma account with Dev Mode access — which most teams are already paying for. Everything else is free, open source, and runs locally.

For teams in regulated industries — anything touching financial data, health records, or GDPR-sensitive information — the local-first nature of this stack matters. Your design tokens and component definitions don't pass through a third-party server. They stay on your machine.


The order is the architecture

Let me be direct about the one thing I see trip teams up most often: starting in the wrong place.

Teams pick tools before establishing workflow order. They find a great Storybook component library and try to adapt it to their Figma file. Or they hook up the Figma MCP server and start generating screens immediately, skipping tokens and primitives entirely.

The sequence that works:

  1. Tokens — export Figma Variables, transform to CSS/Tailwind
  2. Storybook — set up before components exist
  3. Primitives — Button, Input, Typography, Badge using MCP + token config
  4. Patterns — compose primitives into reusable UI blocks
  5. Screens — assemble patterns, not write new styles

If you follow this order, implementing a new screen becomes mostly an assembly task. Select the Figma frame, ask the MCP to implement it, and watch it pull your existing components together. The output is system-compliant by default because the AI knows your component inventory through Code Connect.

This is the workflow I follow when taking on fullstack development projects where the client needs not just a working app, but a codebase their team can actually maintain.


Final thought

The Figma-to-code problem stopped being a tooling problem about a year ago. The free stack — Tokens Studio, Style Dictionary, Storybook, and the Figma MCP server — is genuinely production-ready. What teams lack now isn't better tools. It's the discipline to build in the right order.

Tokens, then Storybook, then primitives, then screens. That's it.

If you're starting a project and want to get the foundation right from day one, a scoping sprint can prevent weeks of refactoring once you're deeper in. The best moment to build a design system is before the first component. The second best moment is right now.


Have questions about implementing this workflow, or working through a specific bottleneck in your design-to-code pipeline? Get in touch.

Pawel Owerczuk
Pawel Owerczuk

AI Agent & RAG Developer

AI Agent & RAG Developer with 10+ years of software engineering experience. Specialized in intelligent AI solutions for enterprises in the DACH & Nordic region.