| name | figma-to-swiftui |
| description | Translate Figma designs into native SwiftUI code (iOS 26 / Swift 6) by reading the Figma file directly through the Figma MCP — no PNG exports or screenshots. Sets up the MCP read→build→preview→compare→iterate loop, maps Figma layout/tokens to native components and HIG values, enumerates screen states, and verifies results numerically with accessibility frames. Use when building or matching Figma frames in SwiftUI, polishing a screen to a comp, batch-importing designed screens, or extracting design tokens. Triggers: "build this Figma frame", "match this Figma design in SwiftUI", "turn my Figma into iOS code", "figma to swiftui", "extract design tokens from Figma". |
Figma → SwiftUI
Get Claude Code to read your Figma designs directly and translate them into
native SwiftUI. No screenshots. No PNG uploads. The Figma MCP gives Claude
design structure — frame dimensions, hierarchy, text content, colors, spacing,
component structure — which you act on, not pixels you guess at.
Know which MCP you're on. The community GLips figma-developer-mcp returns
a parseable layout dump. The official Figma Dev-Mode MCP's
get_design_context returns React JSX + Tailwind CSS by default — that is
design intent, not code to paste. Extract the numbers out of the Tailwind
classes and translate; never transliterate the JSX into Swift. The full
translation tables and value legend are in
references/react-tailwind-to-swiftui.md. (Configure Code Connect —
references/code-connect-swiftui.md — and the official server returns your
actual SwiftUI snippets instead.)
This skill covers the full loop: connect the MCP stack, reference the right
frames, translate to native SwiftUI, map design tokens correctly to HIG
values, cover every screen state, and verify the result numerically before
claiming it's done.
When to use
- You designed a screen in Figma and want Claude Code to build it.
- You're polishing an existing screen to match a refined Figma comp.
- You're adding screens that were designed before being built.
- You're batch-importing several screens from one Figma project.
- You need to extract a design system (colors / type / spacing / radius) out
of a Figma file into Swift tokens.
When NOT to use
- The design lives only as a flat image — ask for the Figma file or frame
link. A PNG loses all structure; the MCP is what makes this skill work.
- You're building from a written description with no design — use a normal
"build screen X" prompt; you don't need Figma reading.
Prerequisites
- Figma MCP connected to Claude Code (see "MCP stack" below).
- Your Figma file organized with named frames, one screen per frame
(not random artboards). Layer names carry into the MCP output — name them
well and Claude gets free structure.
- Ideally the full iOS MCP stack so Claude can build and visually verify its
own output (Sosumi for Apple docs, XcodeBuildMCP for builds, the Xcode MCP
Bridge for preview rendering). See references/mcp-stack-setup.md.
The closed loop (the whole point)
With the full MCP stack, steps 2–8 happen without your intervention —
you describe and review; Claude handles the middle:
1. You point at a Figma frame and describe what it DOES (behavior).
2. Claude reads the frame via Figma MCP (layout, text, color, hierarchy).
3. Claude checks the correct SwiftUI API via Sosumi (HIG, modifiers).
4. Claude writes native SwiftUI.
5. Claude builds via XcodeBuildMCP (catches compile errors).
6. Claude renders a #Preview via the Xcode MCP Bridge (sees its output).
7. Claude compares the preview to the Figma frame.
8. Claude iterates on its own until layout + hierarchy match.
9. You review the final result.
Figma gives the "what it looks like." You give the "what it does."
The MCP provides layout/text/color/hierarchy but not interaction,
navigation, state changes, or animation — those you must describe. That's
why behavior descriptions still matter even with a perfect design read.
How to reference Figma frames
Be specific. Ambiguous references ("the one with the green button") waste a
round trip.
| Method | How |
|---|
| Frame URL | Right-click the frame in Figma → Copy link → paste into your prompt. |
| Node ID | In the Figma URL, the node id appears after node-id=. Reference it directly. |
| File + frame name | "In my Figma file [URL], find the frame called 'Settings Main'." |
Never paste the whole file without naming frames — too much context. Never
reference by visual description — use a URL or node id.
Prompt templates
You do not paste code snippets or Apple-docs links. Your CLAUDE.md
(see "Project setup") carries the standing rules. Describe the screen.
New screen:
Build [Screen Name] based on this Figma frame: [URL or node-id].
Read the frame's layout, text content, spacing, and hierarchy. Use native
SwiftUI components for everything (CLAUDE.md has the component rules). The
Figma design shows visual INTENT — match the layout and hierarchy, but use
system-standard spacing and native component behavior rather than
pixel-exact replication of every dimension.
It does [describe behavior: what tapping/swiping does, where nav goes,
empty/loading/error states].
Polishing an existing screen:
Refine [Screen Name] to match this Figma frame: [URL or node-id].
The screen already works. Adjust layout, spacing, typography weight, and
visual hierarchy to match the Figma design. Do NOT change functionality or
navigation. Do NOT replace native components with custom ones.
Batch import:
Here are the Figma frames for the Settings flow:
- Settings Main: [URL]
- Settings Notifications: [URL]
- Settings Appearance: [URL]
Build all three. Each is a standard grouped List with sections and rows
(CLAUDE.md has navigation patterns). The gear icon on the home screen is
the entry point.
The 80/20 rule — match intent, not pixels
Match the design at the layout and hierarchy level. Don't fight
SwiftUI's layout system to hit every pixel — that produces brittle,
non-adaptive UI that breaks on a different screen size.
Match these:
- Content hierarchy (what's bigger / bolder / more prominent)
- Layout direction (horizontal vs vertical groupings)
- Text content and approximate weight
- Brand/accent color values
- Overall spacing feel (tight vs airy)
Let SwiftUI own these:
- Exact padding values (system defaults are usually right)
- List row heights (system standard beats forced values)
- Safe-area insets, keyboard avoidance
- Navigation-bar and tab-bar styling
- Liquid Glass material on chrome (iOS 26 system default)
The most common failure is a designer's mock specifying 17pt padding and the
agent hardcoding .padding(17) everywhere. Map "tight ~ 8pt, standard ~ 16pt,
section ~ 24pt" to the HIG 8-pt grid (see references/hig-tokens.md),
and reach for .padding() defaults unless the design clearly diverges.
Importing from a non-Figma / web tool? A CSS/web export, a WeChat/WXSS or
Lanhu spec, or an @Nx raster brings two traps Figma doesn't: the numbers are
in a different unit (rpx ÷ 2 → pt, CSS px → pt 1:1, @Nx ÷ N), and a
dimension equal to the canvas/device width is really a fill — emit
.frame(maxWidth: .infinity), never a literal 375/393. CSS-property →
SwiftUI-modifier recipes, the container-type triggers, and the "strip the fake
status bar" rule are in references/web-import-to-swiftui.md.
Native-first (non-negotiable)
Claude's default is to build custom views. Reject that. If SwiftUI has a
native component, use it. Native components inherit Dynamic Type, dark
mode, haptics, system animations, VoiceOver, safe-area handling, and
localization for free; every custom view must re-implement all of it.
The decision test for every element the design shows:
1. Does SwiftUI have a built-in component? → use it
2. Does SwiftUI have a modifier for this? → use it
3. Can native components compose to this? → do that
4. Truly no native way? → custom, but justify + list it
Quick map from "what the Figma shows" to "what to write":
| Figma shows | Use | NOT |
|---|
| A list of items | List / LazyVStack | custom ScrollView+VStack |
| Bottom tabs | TabView | custom HStack of buttons |
| A button opening options | Menu | custom popover |
| A sliding toggle | Toggle | custom switch |
| Segmented choice | Picker (.segmented) | custom segmented control |
| Text input | TextField / TextEditor | custom input |
| A sheet sliding up | .sheet + .presentationDetents | custom modal |
| Long-press options | .contextMenu | custom gesture |
| Search bar | .searchable | custom field |
| Pull to refresh | .refreshable | custom gesture |
| Progress ring/bar | ProgressView / Gauge | custom spinner |
| Alert / confirm | .alert / .confirmationDialog | custom view |
| Grouped settings | Form / List(.insetGrouped) | custom VStack |
| An icon | Image(systemName:) (SF Symbols) | bundled PNG icon |
| Share action | ShareLink | custom share |
| Color picker | ColorPicker | custom grid |
| Paywall content | .subscriptionStoreView | custom purchase UI |
The fuller map and the UIKit-bridge escape hatches live in
references/native-first-and-bridging.md.
Modern state & concurrency (iOS 17+ / Swift 6) the generated code must use:
@Observable (not ObservableObject), @State for local view state,
@Binding for passed-down state, @Query for SwiftData, @Environment
for shared state; async/await + .task (not .onAppear + completion
handlers); @MainActor on view models. Full table in the bridging reference.
Red flags in generated output (even if you can't read Swift):
UIViewRepresentable/UIHostingController where unnecessary, GeometryReader
for basic layouts, Custom<Thing> when a native component exists, hardcoded
.frame(width: 393), custom tap/long-press gesture recognizers.
Cover every state — not just the happy path
A Figma frame is one state. Real screens have many. Before you call a screen
done, enumerate the states the design (and the behavior you described)
implies, and build them. The minimum set per screen plus the per-type
additions (forms, lists, detail, flow, permission, first-run, dark mode) is
in references/screen-states-checklist.md.
The two most-forgotten states, every time:
- Permission states for anything touching camera / photos / location /
notifications: Granted / Denied / NotDetermined / Prompting.
- Empty / Loading / Error for any data-backed screen.
In SwiftUI, don't build light + dark duplicates — bind colors to the asset
catalog / Color semantic colors and let the system switch. Build a single
"Theme Showcase" preview for a light/dark spot-check.
Extracting design tokens from a Figma file
When the task is "pull the design system out of Figma" (colors, type,
spacing, radius) rather than "build one screen", the discipline is:
- Register what already exists — Figma Variables / Styles / Components
already in the file. Read them; don't recreate them.
- Find recurring raw values — a color / type pair / radius / spacing
value used ≥ 3 times is a token candidate. A one-off is not.
- Map to HIG semantics — emit Swift tokens named for their role
(
label, secondaryLabel, accent, systemBackground, spacing s16,
radius r12), backed by the asset catalog for light/dark, not literal
hexes scattered through views.
- Never invent values — if the design doesn't specify a color, use the
iOS system default. Only define what diverges.
The full extraction recipe (with the Figma Plugin-API scan code and the
≥3-occurrence dedup logic) is in references/figma-token-extraction.md. The
HIG hard data you map onto (typography scale, 8-pt spacing, radius, touch
targets, semantic colors, safe areas, shadows, the Figma-shadow→native blur÷2
rule) is in references/hig-tokens.md.
The MCP gives layout + tokens but cannot export asset bytes — for the
actual cover images / illustrations / tintable icons going into
Assets.xcassets, pull them via the Figma REST v1/images endpoint:
references/asset-bytes-rest.md (two round-trips, hyphen→colon node ids,
per-scale imageset loop, chip-strip for template SVGs, token hygiene).
Project setup that makes this reliable
Add a ## Build and Preview Loop block to your project CLAUDE.md so Claude
self-verifies before showing you anything:
## Build and Preview Loop
After building or modifying any screen:
1. Build with XcodeBuildMCP. Fix any compile errors.
2. Render a #Preview with RenderPreview. Check the layout.
3. If you provided a Figma frame, compare the preview to the design.
4. Only tell me the screen is ready after the build succeeds AND the
preview looks correct.
## Component Rules
- Use native SwiftUI components for everything unless a custom component is
explicitly listed in the Custom Components section.
- Before creating a custom view, check for a built-in component first.
- Do not wrap native components in custom abstractions.
- Use @Observable, @State/@Binding, async/await + .task, @MainActor on VMs.
## Visual Design
See DESIGN_SYSTEM.md for colors, typography, and assets. When in doubt, use
iOS system defaults; apply custom values only where DESIGN_SYSTEM.md defines
them.
This drops most screens from 2–3 feedback rounds to 0–1. Keep CLAUDE.md
under ~500 lines — Claude loads the whole file every session, and bloat
dilutes the rules that matter. The DESIGN_SYSTEM.md shape (define only what
diverges from iOS defaults) is in references/design-system-doc.md.
Never let Claude modify .pbxproj files when adding new screen files —
that corrupts the project. Add files through Xcode or a tool that knows the
project format.
Verify numerically before claiming "done"
Eyeballing a preview is unreliable for layout. Humans cannot see 16pt vs
21pt padding, 2pt off-center drift, or 40pt vs 44pt button heights — all of
which are real bugs that ship in apps that "looked fine." Replace "looks
right" with a number.
After building from a frame:
- Build clean via XcodeBuildMCP (SUCCEEDED, zero errors). Ignore phantom
SourceKit diagnostics that don't appear in the build log — that's lag.
- Screenshot / render the preview — gut check, never sufficient alone.
- AX snapshot (
snapshot_ui returns every visible element's AXFrame
x/y/width/height in real screen coordinates). Verify layout with math:
- Is the child centered in / aligned to / offset from its parent?
- Is spacing between siblings equal?
- Is anything off-screen?
- Does padding match the design spec?
Button AXFrame: x=80, width=242 on a 402pt-wide screen
right edge = 80 + 242 = 322 ; right margin = 402 - 322 = 80
left margin = 80 → difference 0 → centered ✓
If the math doesn't check out, the fix isn't done. Centered is a number,
not a feeling.
- Commit only after the numbers match.
Drive any verification taps by accessibility id / label, not coordinates
— coordinates break the moment layout shifts; labels survive. Run the check
on more than one device width (SE, mini, and a Pro Max) — layout bugs love
the narrow screens. Full loop (with simulator-state-reset recipes for
first-run flows) in references/verification-loop.md.
Anti-patterns
| Temptation | Why it fails |
|---|
| "Match every pixel exactly" | You fight SwiftUI's layout and ship brittle, non-adaptive UI |
| Exporting PNGs and uploading them | Loses all structure; the MCP gives design structure (layout dump or React+Tailwind) you can act on |
Pasting the official MCP's React+Tailwind output into a .swift file | It's design intent, not code — extract the values, translate via references/react-tailwind-to-swiftui.md |
| Referencing frames by description | Ambiguous; use URLs or node ids |
| Sending the whole Figma file | Too much context; reference specific frames |
| Expecting interactions inferred from a static frame | Frames are static; always describe what taps/swipes/nav do |
| Hardcoding the design's literal padding/hex everywhere | Map to the 8-pt grid + semantic tokens; let .padding() defaults work |
| Building only the Default state | Ship Empty/Loading/Error + permission + dark mode too |
| Claiming a layout fix done on a screenshot gut-check | Verify the AXFrame math; the "obvious" fix is where regressions hide |
Letting Claude edit .pbxproj | Corrupts the project; add files through Xcode |
References
- references/react-tailwind-to-swiftui.md — translate the official MCP's React+Tailwind output: the Tailwind value legend, full Flexbox/Grid/color/type/border/shadow tables, iOS 26 glass indicators, CSS/Framer-Motion → SwiftUI, and a Tailwind→UIKit table.
- references/mcp-stack-setup.md — connect Figma MCP (GLips vs official) + Sosumi + XcodeBuildMCP + Xcode MCP Bridge; the official server's 16 tools / rate limits / URL parsing; extraction validation + re-extract triggers; the
mcpbridge tool surface; failure playbook; the SwiftUI→Figma reverse workflow.
- references/code-connect-swiftui.md —
FigmaConnect structs + property decorators so get_design_context returns your SwiftUI directly (the acceleration layer; requires the official MCP).
- references/asset-bytes-rest.md — pull raster/vector asset BYTES via the Figma REST
v1/images endpoint (the MCP can't export bytes): imageset loop, tintable-SVG setup, chip-strip, token hygiene.
- references/web-import-to-swiftui.md — importing from a non-Figma source (CSS/web, WXSS/Lanhu rpx,
@Nx): unit conversion, canvas-width=fill rule, CSS→SwiftUI recipes, container-type triggers, strip the fake status bar.
- references/hig-tokens.md — HIG hard data: typography scale, 8-pt spacing, radius, 44×44 touch targets, semantic colors (light/dark), safe areas, shadows (incl. Figma blur÷2 + alpha separation), the UIKit Figma-fidelity tolerance gate, font availability + EN↔CJK weight equivalence.
- references/native-first-and-bridging.md — full native-component map, container-type triggers, modern state/concurrency rules, and the three legitimate UIKit-bridge escape hatches.
- references/screen-states-checklist.md — per-screen-type state enumeration so no state ships missing.
- references/figma-token-extraction.md — extract a design system from a Figma file: Plugin-API scan code + ≥3-occurrence dedup.
- references/design-system-doc.md — the
DESIGN_SYSTEM.md shape (define only what diverges from iOS defaults) + the brand-sourcing Ask→Search→Download→Verify→Write flow.
- references/verification-loop.md — the build→screenshot→AX→math-check loop and simulator-state-reset recipes.