Standardize Figma-to-code workflow using Figma MCP - always get_design_context first, then screenshot, use project tokens not hardcoded values, validate 1:1 parity
This skill standardizes the Figma-to-code workflow using the Figma MCP (Model Context Protocol). Follow this process to ensure high-fidelity implementations that respect project conventions and design systems.
The workflow consists of 6 mandatory steps that must be executed in order:
ALWAYS start with get_design_context to retrieve structured node data including styles, layout, and component hierarchy.
// Call the Figma MCP tool
mcp__figma-desktop__get_design_context({
nodeId: "123:456", // Extract from URL or use current selection
clientLanguages: "typescript,javascript",
clientFrameworks: "react"
})
What you'll receive:
Important: The returned React + Tailwind code is a design reference, not production-ready code. You must adapt it to project conventions.
If the response is too large and gets truncated, you'll see a message indicating this. When this happens:
mcp__figma-desktop__get_metadata({
nodeId: "123:456", // Parent node or page
clientLanguages: "typescript,javascript",
clientFrameworks: "react"
})
Identify specific child nodes from the XML structure
Re-fetch each node individually using get_design_context:
// Fetch specific nodes that were truncated
mcp__figma-desktop__get_design_context({
nodeId: "123:789", // Specific child node
clientLanguages: "typescript,javascript",
clientFrameworks: "react"
})
Always capture a screenshot for visual validation:
mcp__figma-desktop__get_screenshot({
nodeId: "123:456",
clientLanguages: "typescript,javascript",
clientFrameworks: "react"
})
Use the screenshot to:
If Figma MCP returns localhost sources for images or SVGs:
DO:
public/assets/, src/assets/)DO NOT:
Example:
// Figma MCP provides: http://localhost:PORT/asset.svg
// Download and save to: /public/assets/icons/logo.svg
// Use in code:
<img src="/assets/icons/logo.svg" alt="Logo" />
This is where you translate the Figma design into production code. Follow these rules:
NEVER hardcode colors. Use project tokens from src/index.css (Tailwind v4 @theme block).
Wrong:
<div className="bg-blue-500 text-white border-gray-200">
Correct:
<div className="bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))] border-[hsl(var(--color-border))]">
Common project tokens:
/* From src/index.css @theme block */
--color-background: /* Page background */
--color-foreground: /* Main text color */
--color-primary: /* Brand primary */
--color-primary-foreground: /* Text on primary */
--color-secondary: /* Secondary accent */
--color-secondary-foreground: /* Text on secondary */
--color-muted: /* Muted backgrounds */
--color-muted-foreground: /* Muted text */
--color-accent: /* Accent color */
--color-accent-foreground: /* Text on accent */
--color-border: /* Border color */
--color-input: /* Input borders */
--color-ring: /* Focus rings */
--color-destructive: /* Error/delete */
--color-destructive-foreground: /* Text on destructive */
Token usage examples:
// Backgrounds
className="bg-[hsl(var(--color-background))]"
className="bg-[hsl(var(--color-primary))]"
className="bg-[hsl(var(--color-muted))]"
// Text colors
className="text-[hsl(var(--color-foreground))]"
className="text-[hsl(var(--color-primary))]"
className="text-[hsl(var(--color-muted-foreground))]"
// Borders
className="border-[hsl(var(--color-border))]"
className="border-[hsl(var(--color-input))]"
// Focus states
className="focus:ring-[hsl(var(--color-ring))]"
NEVER reinvent shadcn components. Check components/ui/ first.
Available components:
Button - All button variantsCard, CardHeader, CardTitle, CardDescription, CardContent, CardFooterInput, Textarea, SelectBadge - Labels and tagsDialog, Sheet, Popover, DropdownMenuTabs, Accordion, CollapsibleAvatar, Separator, SkeletonToast, Alert, AlertDialogWrong:
<div className="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-[hsl(var(--color-primary-foreground))] hover:bg-[hsl(var(--color-primary))]/90">
Click me
</div>
Correct:
import { Button } from "@/components/ui/button"
<Button>Click me</Button>
Component reuse checklist:
components/ui/Typography scale:
// Headings
<h1 className="text-4xl font-bold">
<h2 className="text-3xl font-semibold">
<h3 className="text-2xl font-semibold">
<h4 className="text-xl font-semibold">
// Body text
<p className="text-base">
<p className="text-sm text-[hsl(var(--color-muted-foreground))]">
<p className="text-xs text-[hsl(var(--color-muted-foreground))]">
Spacing scale:
// Use Tailwind's spacing scale (based on 0.25rem units)
gap-4 // 1rem
gap-6 // 1.5rem
gap-8 // 2rem
p-4 // padding: 1rem
px-6 // padding-left/right: 1.5rem
py-8 // padding-top/bottom: 2rem
space-y-4 // vertical spacing between children
Routing:
State Management:
File Structure:
src/components/src/pages/src/lib/src/types/TypeScript:
any typesBefore marking implementation complete, validate against the Figma screenshot:
Visual Parity:
Code Quality:
Accessibility:
Performance:
User provides Figma URL: https://figma.com/design/ABC123/MyApp?node-id=1-2
Step 1: Extract node ID and fetch design context
Node ID: 1:2
Call get_design_context with:
Step 2: Analyze response Response includes:
Step 3: Get screenshot
Call get_screenshot with nodeId "1:2" for visual reference
Step 4: Download assets
If icon is provided as localhost source, download to public/assets/icons/
Step 5: Implement
Wrong approach (using Figma MCP output as-is):
// Don't do this!
<div className="bg-blue-500 text-white rounded-lg p-6">
<h2 className="text-2xl font-bold mb-2">Welcome</h2>
<p className="text-gray-100 mb-4">Get started with our platform</p>
<button className="bg-white text-blue-500 px-4 py-2 rounded-md flex items-center gap-2">
Get Started
<ChevronRight /> {/* New icon import! */}
</button>
</div>
Correct approach (project conventions):
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
export function WelcomeCard() {
return (
<Card className="bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]">
<CardHeader>
<CardTitle className="text-2xl">Welcome</CardTitle>
<CardDescription className="text-[hsl(var(--color-primary-foreground))]/90">
Get started with our platform
</CardDescription>
</CardHeader>
<CardContent>
<Button
variant="secondary"
className="flex items-center gap-2"
>
Get Started
<img
src="/assets/icons/chevron-right.svg"
alt=""
className="w-4 h-4"
/>
</Button>
</CardContent>
</Card>
)
}
Step 6: Validate
// WRONG
<div className="bg-blue-500 text-white border-gray-200">
// CORRECT
<div className="bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))] border-[hsl(var(--color-border))]">
// WRONG
import { ChevronRight } from "lucide-react"
// CORRECT - Use asset from Figma
<img src="/assets/icons/chevron-right.svg" alt="" className="w-4 h-4" />
// WRONG
<div className="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold">
New
</div>
// CORRECT
import { Badge } from "@/components/ui/badge"
<Badge>New</Badge>
// WRONG - Implementing without screenshot reference
// CORRECT - Always get screenshot and validate against it
// WRONG
<div className="w-full h-64 bg-gray-200 flex items-center justify-center">
Image placeholder
</div>
// CORRECT - Use provided localhost asset
<img src="/assets/images/hero.png" alt="Hero" className="w-full h-64 object-cover" />
| Figma MCP Output | Project Token |
|---|---|
bg-blue-500 |
bg-[hsl(var(--color-primary))] |
text-white |
text-[hsl(var(--color-primary-foreground))] |
bg-gray-100 |
bg-[hsl(var(--color-muted))] |
text-gray-600 |
text-[hsl(var(--color-muted-foreground))] |
border-gray-200 |
border-[hsl(var(--color-border))] |
bg-red-500 |
bg-[hsl(var(--color-destructive))] |
| Figma MCP Pattern | shadcn Component |
|---|---|
| Card with header/body | Card, CardHeader, CardContent |
| Button | Button with variants |
| Input field | Input or Textarea |
| Dropdown | Select or DropdownMenu |
| Modal | Dialog or Sheet |
| Tag/Label | Badge |
| Divider | Separator |
| Loading state | Skeleton |
get_design_contextget_metadataget_screenshotAlways remember:
By following this workflow, you'll ensure consistent, maintainable implementations that respect both the design system and project conventions.