Use Claude Code for Figma-to-Code Workflow 2026
The Workflow
Convert Figma design files into production-ready React components using Claude Code with the Figma MCP server. This workflow extracts design tokens, generates typed component code, and produces matching CSS that stays synchronized with design changes.
Expected time: 20-45 minutes for a component library page Prerequisites: Figma account with Developer mode access, Figma Personal Access Token, Claude Code installed, Node.js 18+
Setup
1. Install the Figma MCP Server
npm install -g @anthropic/mcp-server-figma
Configure Claude Code to use it by adding to your MCP settings:
{
"mcpServers": {
"figma": {
"command": "mcp-server-figma",
"env": {
"FIGMA_ACCESS_TOKEN": "figd_YOUR_TOKEN_HERE"
}
}
}
}
2. Create a Design Token Extraction Config
mkdir -p src/design-system && cat > src/design-system/figma.config.json << 'EOF'
{
"fileKey": "YOUR_FIGMA_FILE_KEY",
"pages": ["Design System", "Components"],
"tokenCategories": ["colors", "typography", "spacing", "shadows", "radii"],
"componentPrefix": "UI/",
"outputDir": "src/components/ui"
}
EOF
The file key is the string between /file/ and / in your Figma URL.
3. Verify MCP Connection
claude --mcp-list
# Expected output:
# figma: connected (mcp-server-figma)
Usage Example
With the Figma MCP connected, Claude Code can read your design file directly. Here is a complete workflow that extracts a Button component:
First, extract design tokens from Figma and generate CSS variables:
/* src/design-system/tokens.css */
/* Generated from Figma file: Design System v2.4 */
:root {
/* Colors - Primary */
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
/* Colors - Neutral */
--color-neutral-50: #f9fafb;
--color-neutral-100: #f3f4f6;
--color-neutral-200: #e5e7eb;
--color-neutral-700: #374151;
--color-neutral-900: #111827;
/* Typography */
--font-sans: 'Inter', -apple-system, sans-serif;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
/* Spacing */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
/* Border Radius */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-full: 9999px;
}
Then generate the typed React component matching the Figma design:
// src/components/ui/Button.tsx
import React from 'react';
import styles from './Button.module.css';
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
fullWidth?: boolean;
}
export const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
size = 'md',
loading = false,
leftIcon,
rightIcon,
fullWidth = false,
disabled,
className,
...props
}) => {
const classes = [
styles.button,
styles[variant],
styles[size],
fullWidth ? styles.fullWidth : '',
loading ? styles.loading : '',
className || '',
].filter(Boolean).join(' ');
return (
<button
className={classes}
disabled={disabled || loading}
{...props}
>
{loading && <span className={styles.spinner} aria-hidden="true" />}
{!loading && leftIcon && <span className={styles.icon}>{leftIcon}</span>}
<span className={styles.label}>{children}</span>
{!loading && rightIcon && <span className={styles.icon}>{rightIcon}</span>}
</button>
);
};
/* src/components/ui/Button.module.css */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
font-family: var(--font-sans);
font-weight: var(--font-weight-semibold);
border-radius: var(--radius-md);
border: 1px solid transparent;
cursor: pointer;
transition: background-color 150ms ease, box-shadow 150ms ease;
}
.button:focus-visible {
outline: 2px solid var(--color-primary-500);
outline-offset: 2px;
}
/* Sizes from Figma */
.sm { padding: var(--space-1) var(--space-3); font-size: var(--font-size-sm); }
.md { padding: var(--space-2) var(--space-4); font-size: var(--font-size-base); }
.lg { padding: var(--space-3) var(--space-6); font-size: var(--font-size-lg); }
/* Variants from Figma */
.primary {
background-color: var(--color-primary-600);
color: white;
}
.primary:hover { background-color: var(--color-primary-700); }
.secondary {
background-color: white;
color: var(--color-neutral-700);
border-color: var(--color-neutral-200);
}
.secondary:hover { background-color: var(--color-neutral-50); }
.ghost {
background-color: transparent;
color: var(--color-neutral-700);
}
.ghost:hover { background-color: var(--color-neutral-100); }
.danger {
background-color: #dc2626;
color: white;
}
.danger:hover { background-color: #b91c1c; }
.fullWidth { width: 100%; }
.loading { opacity: 0.7; pointer-events: none; }
.spinner {
width: 1em;
height: 1em;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: var(--radius-full);
animation: spin 600ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.icon { display: inline-flex; flex-shrink: 0; }
.label { white-space: nowrap; }
Common Issues
- Figma API returns 403: Your Personal Access Token needs read access to the file. Generate a new token at figma.com/developers with File Content scope enabled.
- Design tokens drift from Figma: Run token extraction on a schedule or as a pre-commit hook. Compare generated tokens.css against the committed version to detect drift.
- Component styles do not match Figma: Check that you are reading from the correct Figma page and frame. Use Developer mode in Figma to verify exact pixel values, then match with CSS variables.
Why This Matters
Manual design-to-code translation introduces visual inconsistencies and takes hours per component. Automated extraction with Claude Code keeps code and design in sync with measurable pixel accuracy.