Use this skill to create or update full-page screens in Figma by
reusing the published design system
— components, variables, and styles — rather than drawing primitives with hardcoded values. The key insight: the Figma file likely has a published design system with components, color/spacing variables, and text/effect styles that correspond to the codebase's UI components and tokens. Find and use those instead of drawing boxes with hex colors.
MANDATORY
You MUST also load
figma-use
before any
use_figma
call. That skill contains critical rules (color ranges, font loading, etc.) that apply to every script you write.
Always pass
skillNames: "figma-generate-design"
when calling
use_figma
as part of this skill.
This is a logging parameter — it does not affect execution.
Skill Boundaries
Use this skill when the deliverable is a
Figma screen
(new or updated) composed of design system component instances.
If the user wants to generate
code from a Figma design
, switch to
figma-implement-design
.
If the user wants to create
new reusable components or variants
, use
figma-use
directly.
If the user wants to write
Code Connect mappings
, switch to
figma-code-connect
.
Prerequisites
Figma MCP server must be connected
The target Figma file must have a published design system with components (or access to a team library)
User should provide either:
A Figma file URL / file key to work in
Or context about which file to target (the agent can discover pages)
Source code or description of the screen to build/update
Parallel Workflow with generate_figma_design (Web Apps Only)
When building a screen from a
web app
that can be rendered in a browser, the best results come from running both approaches in parallel:
In parallel:
Start building the screen using this skill's workflow (use_figma + design system components)
Run
generate_figma_design
to capture a pixel-perfect screenshot of the running web app
Once both complete:
Update the use_figma output to match the pixel-perfect layout from the
generate_figma_design
capture. The capture provides the exact spacing, sizing, and visual treatment to aim for, while your use_figma output has proper component instances linked to the design system. If the capture contains images, transfer them to your use_figma output by copying
imageHash
values from the capture's image fills (see Step 5 for details).
Once confirmed looking good:
Delete the
generate_figma_design
output — it was only used as a visual reference.
This combines the best of both:
generate_figma_design
gives pixel-perfect layout accuracy, while use_figma gives proper design system component instances that stay linked and updatable.
This parallel workflow is MANDATORY when the source contains images.
The
use_figma
Plugin API cannot fetch external image URLs — it can only set image fills by copying
imageHash
values from nodes already in the file.
generate_figma_design
rasterizes all visible images into Figma, providing the hashes you need. If you skip the capture when images are present, image frames will be left blank.
For non-web apps (iOS, Android, etc.) or when updating existing screens, use the standard workflow below.
Required Workflow
Follow these steps in order. Do not skip steps.
Step 1: Understand the Screen
Before touching Figma, understand what you're building:
If building from code, read the relevant source files to understand the page structure, sections, and which components are used.
Identify the major sections of the screen (e.g., Header, Hero, Content Panels, Pricing Grid, FAQ Accordion, Footer).
For each section, list the UI components involved (buttons, inputs, cards, navigation pills, accordions, etc.).
Check whether the screen contains any images
(e.g.,
,
, background images, product photos, avatars, icons loaded from URLs). If it does and this is a web app, you
must
run the parallel
generate_figma_design
capture workflow — start it immediately alongside Step 2 so the capture runs while you discover components. See "Parallel Workflow with generate_figma_design" above.
Step 2: Discover Design System — Components, Variables, and Styles
You need three things from the design system:
components
(buttons, cards, etc.),
variables
(colors, spacing, radii), and
styles
(text styles, effect styles like shadows). Don't hardcode hex colors or pixel values when design system tokens exist.
2a: Discover components
Preferred: inspect existing screens first.
If the target file already contains screens using the same design system, skip
search_design_system
and inspect existing instances directly. A single
use_figma
call that walks an existing frame's instances gives you an exact, authoritative component map:
const
frame
=
figma
.
currentPage
.
findOne
(
n
=>
n
.
name
===
"Existing Screen"
)
;
const
uniqueSets
=
new
Map
(
)
;
frame
.
findAll
(
n
=>
n
.
type
===
"INSTANCE"
)
.
forEach
(
inst
=>
{
const
mc
=
inst
.
mainComponent
;
const
cs
=
mc
?.
parent
?.
type
===
"COMPONENT_SET"
?
mc
.
parent
:
null
;
const
key
=
cs
?
cs
.
key
:
mc
?.
key
;
const
name
=
cs
?
cs
.
name
:
mc
?.
name
;
if
(
key
&&
!
uniqueSets
.
has
(
key
)
)
{
uniqueSets
.
set
(
key
,
{
name
,
key
,
isSet
:
!
!
cs
,
sampleVariant
:
mc
.
name
}
)
;
}
}
)
;
return
[
...
uniqueSets
.
values
(
)
]
;
Only fall back to
search_design_system
when the file has no existing screens to reference. When using it,
search broadly
— try multiple terms and synonyms (e.g., "button", "input", "nav", "card", "accordion", "header", "footer", "tag", "avatar", "toggle", "icon", etc.). Use
includeComponents: true
to focus on components.
Include component properties
in your map — you need to know which TEXT properties each component exposes for text overrides. Create a temporary instance, read its
componentProperties
(and those of nested instances), then remove the temp instance.
If initial searches return empty, try shorter fragments or different naming conventions — libraries vary widely ("grey" vs "gray", "spacing" vs "space", "color/bg" vs "background").
Inspect an existing screen's bound variables for the most authoritative results:
const
frame
=
figma
.
currentPage
.
findOne
(
n
=>
n
.
name
===
"Existing Screen"
)
;
const
varMap
=
new
Map
(
)
;
frame
.
findAll
(
(
)
=>
true
)
.
forEach
(
node
=>
{
const
bv
=
node
.
boundVariables
;
if
(
!
bv
)
return
;
for
(
const
[
prop
,
binding
]
of
Object
.
entries
(
bv
)
)
{
const
bindings
=
Array
.
isArray
(
binding
)
?
binding
:
[
binding
]
;
for
(
const
b
of
bindings
)
{
if
(
b
?.
id
&&
!
varMap
.
has
(
b
.
id
)
)
{
const
v
=
await
figma
.
variables
.
getVariableByIdAsync
(
b
.
id
)
;
if
(
v
)
varMap
.
set
(
b
.
id
,
{
name
:
v
.
name
,
id
:
v
.
id
,
key
:
v
.
key
,
type
:
v
.
resolvedType
,
remote
:
v
.
remote
}
)
;
}
}
}
}
)
;
return
[
...
varMap
.
values
(
)
]
;
For library variables (remote = true), import them by key with
figma.variables.importVariableByKeyAsync(key)
. For local variables, use
figma.variables.getVariableByIdAsync(id)
directly.
See
variable-patterns.md
for binding patterns.
2c: Discover styles (text styles, effect styles)
Search for styles using
search_design_system
with
includeStyles: true
and terms like "heading", "body", "shadow", "elevation". Or inspect what an existing screen uses:
const
frame
=
figma
.
currentPage
.
findOne
(
n
=>
n
.
name
===
"Existing Screen"
)
;
const
styles
=
{
text
:
new
Map
(
)
,
effect
:
new
Map
(
)
}
;
frame
.
findAll
(
(
)
=>
true
)
.
forEach
(
node
=>
{
if
(
'textStyleId'
in
node
&&
node
.
textStyleId
)
{
const
s
=
figma
.
getStyleById
(
node
.
textStyleId
)
;
if
(
s
)
styles
.
text
.
set
(
s
.
id
,
{
name
:
s
.
name
,
id
:
s
.
id
,
key
:
s
.
key
}
)
;
}
if
(
'effectStyleId'
in
node
&&
node
.
effectStyleId
)
{
const
s
=
figma
.
getStyleById
(
node
.
effectStyleId
)
;
if
(
s
)
styles
.
effect
.
set
(
s
.
id
,
{
name
:
s
.
name
,
id
:
s
.
id
,
key
:
s
.
key
}
)
;
}
}
)
;
return
{
textStyles
:
[
...
styles
.
text
.
values
(
)
]
,
effectStyles
:
[
...
styles
.
effect
.
values
(
)
]
}
;
Import library styles with
figma.importStyleByKeyAsync(key)
, then apply with
node.textStyleId = style.id
or
node.effectStyleId = style.id
.
See
text-style-patterns.md
and
effect-style-patterns.md
for details.
Step 3: Create the Page Wrapper Frame First
Do NOT build sections as top-level page children and reparent them later
— moving nodes across
use_figma
calls with
appendChild()
silently fails and produces orphaned frames. Instead, create the wrapper first, then build each section directly inside it.
Create the page wrapper in its own
use_figma
call. Position it away from existing content and return its ID:
// Find clear space
let
maxX
=
0
;
for
(
const
child
of
figma
.
currentPage
.
children
)
{
maxX
=
Math
.
max
(
maxX
,
child
.
x
+
child
.
width
)
;
}
const
wrapper
=
figma
.
createAutoLayout
(
"VERTICAL"
)
;
wrapper
.
name
=
"Homepage"
;
wrapper
.
primaryAxisAlignItems
=
"CENTER"
;
wrapper
.
counterAxisAlignItems
=
"CENTER"
;
wrapper
.
resize
(
1440
,
100
)
;
wrapper
.
layoutSizingHorizontal
=
"FIXED"
;
wrapper
.
x
=
maxX
+
200
;
wrapper
.
y
=
0
;
return
{
success
:
true
,
wrapperId
:
wrapper
.
id
}
;
Step 4: Build Each Section Inside the Wrapper
This is the most important step.
Build one section at a time, each in its own
use_figma
call. At the start of each script, fetch the wrapper by ID and append new content directly to it.
const
createdNodeIds
=
[
]
;
const
wrapper
=
await
figma
.
getNodeByIdAsync
(
"WRAPPER_ID_FROM_STEP_3"
)
;
// Import design system components by key
const
buttonSet
=
await
figma
.
importComponentSetByKeyAsync
(
"BUTTON_SET_KEY"
)
;
const
primaryButton
=
buttonSet
.
children
.
find
(
c
=>
c
.
type
===
"COMPONENT"
&&
c
.
name
.
includes
(
"variant=primary"
)
)
||
buttonSet
.
defaultVariant
;
// Import design system variables for colors and spacing
const
bgColorVar
=
await
figma
.
variables
.
importVariableByKeyAsync
(
"BG_COLOR_VAR_KEY"
)
;
const
spacingVar
=
await
figma
.
variables
.
importVariableByKeyAsync
(
"SPACING_VAR_KEY"
)
;
// Build section frame with variable bindings (not hardcoded values)
const
section
=
figma
.
createAutoLayout
(
)
;
section
.
name
=
"Header"
;
section
.
setBoundVariable
(
"paddingLeft"
,
spacingVar
)
;
section
.
setBoundVariable
(
"paddingRight"
,
spacingVar
)
;
const
bgPaint
=
figma
.
variables
.
setBoundVariableForPaint
(
{
type
:
'SOLID'
,
color
:
{
r
:
0
,
g
:
0
,
b
:
0
}
}
,
'color'
,
bgColorVar
)
;
section
.
fills
=
[
bgPaint
]
;
// Import and apply text/effect styles
const
shadowStyle
=
await
figma
.
importStyleByKeyAsync
(
"SHADOW_STYLE_KEY"
)
;
section
.
effectStyleId
=
shadowStyle
.
id
;
// Create component instances inside the section
const
btnInstance
=
primaryButton
.
createInstance
(
)
;
section
.
appendChild
(
btnInstance
)
;
createdNodeIds
.
push
(
btnInstance
.
id
)
;
// Append section to wrapper
wrapper
.
appendChild
(
section
)
;
section
.
layoutSizingHorizontal
=
"FILL"
;
// AFTER appending
createdNodeIds
.
push
(
section
.
id
)
;
return
{
success
:
true
,
createdNodeIds
}
;
After each section, validate with
get_screenshot
before moving on. Look closely for cropped/clipped text (line heights cutting off content) and overlapping elements — these are the most common issues and easy to miss at a glance.
Override instance text with setProperties()
Component instances ship with placeholder text ("Title", "Heading", "Button"). Use the component property keys you discovered in Step 2 to override them with
setProperties()
— this is more reliable than direct
node.characters
manipulation. See
component-patterns.md
for the full pattern.
For nested instances that expose their own TEXT properties, call
setProperties()
on the nested instance:
const
nestedHeading
=
cardInstance
.
findOne
(
n
=>
n
.
type
===
"INSTANCE"
&&
n
.
name
===
"Text Heading"
)
;
if
(
nestedHeading
)
{
nestedHeading
.
setProperties
(
{
"Text#2104:5"
:
"Actual heading from source code"
}
)
;
}
Only fall back to direct
node.characters
for text that is NOT managed by any component property.
Read source code defaults carefully
When translating code components to Figma instances, check the component's default prop values in the source code, not just what's explicitly passed. For example,
with no variant prop — check the component definition to find
variant = "primary"
as the default. Selecting the wrong variant (e.g., Neutral instead of Primary) produces a visually incorrect result that's easy to miss.
What to build manually vs. import from design system
shadows, blurs, etc.
Never hardcode hex colors or pixel spacing
when a design system variable exists. Use
setBoundVariable
for spacing/radii and
setBoundVariableForPaint
for colors. Apply text styles with
node.textStyleId
and effect styles with
node.effectStyleId
.
Step 5: Validate the Full Screen and Transfer Images
After composing all sections, call
get_screenshot
on the full page frame and compare against the source. Fix any issues with targeted
use_figma
calls — don't rebuild the entire screen.
Screenshot individual sections, not just the full page.
A full-page screenshot at reduced resolution hides text truncation, wrong colors, and placeholder text that hasn't been overridden. Take a screenshot of each section by node ID to catch:
Cropped/clipped text
— line heights or frame sizing cutting off descenders, ascenders, or entire lines
Overlapping content
— elements stacking on top of each other due to incorrect sizing or missing auto-layout
Placeholder text still showing ("Title", "Heading", "Button")
Truncated content from layout sizing bugs
Wrong component variants (e.g., Neutral vs Primary button)
Blank image placeholders
— if images are missing, you need to transfer them from the
generate_figma_design
capture (see below)
Transfer images from the generate_figma_design capture
If you ran
generate_figma_design
in parallel (mandatory when the source contains images), transfer the captured images into your design system output:
Find all image nodes in the capture output by searching for fills with
type === "IMAGE"
:
const
capture
=
await
figma
.
getNodeByIdAsync
(
"CAPTURE_NODE_ID"
)
;
const
imageNodes
=
[
]
;
capture
.
findAll
(
n
=>
{
if
(
n
.
fills
&&
Array
.
isArray
(
n
.
fills
)
)
{
for
(
const
fill
of
n
.
fills
)
{
if
(
fill
.
type
===
"IMAGE"
)
{
imageNodes
.
push
(
{
name
:
n
.
name
,
id
:
n
.
id
,
imageHash
:
fill
.
imageHash
}
)
;
return
true
;
}
}
}
return
false
;
}
)
;
return
imageNodes
;
Match each captured image to the corresponding frame in your use_figma output (by position, name, or order).
Apply the image hash to the target frame:
targetFrame
.
fills
=
[
{
type
:
"IMAGE"
,
imageHash
:
"hash_from_capture"
,
scaleMode
:
"FILL"
}
]
;
Delete the
generate_figma_design
capture output after all images are transferred.
Step 6: Updating an Existing Screen
When updating rather than creating from scratch:
Use
get_metadata
to inspect the existing screen structure.
Identify which sections need updating and which can stay.
For each section that needs changes:
Locate the existing nodes by ID or name
Swap component instances if the design system component changed
Update text content, variant properties, or layout as needed
Remove deprecated sections
Add new sections
Validate with
get_screenshot
after each modification.
// Example: Swap a button variant in an existing screen
const
existingButton
=
await
figma
.
getNodeByIdAsync
(
"EXISTING_BUTTON_INSTANCE_ID"
)
;
if
(
existingButton
&&
existingButton
.
type
===
"INSTANCE"
)
{
// Import the updated component
const
buttonSet
=
await
figma
.
importComponentSetByKeyAsync
(
"BUTTON_SET_KEY"
)
;
const
newVariant
=
buttonSet
.
children
.
find
(
c
=>
c
.
name
.
includes
(
"variant=primary"
)
&&
c
.
name
.
includes
(
"size=lg"
)
)
||
buttonSet
.
defaultVariant
;
existingButton
.
swapComponent
(
newVariant
)
;
}
return
{
success
:
true
,
mutatedNodeIds
:
[
existingButton
.
id
]
}
;
Reference Docs
For detailed API patterns and gotchas, load these from the
figma-use
references as needed:
component-patterns.md
— importing by key, finding variants, setProperties, text overrides, working with instances
variable-patterns.md
— creating/binding variables, importing library variables, scopes, aliasing, discovering existing variables
text-style-patterns.md
— creating/applying text styles, importing library text styles, type ramps
effect-style-patterns.md
— creating/applying effect styles (shadows), importing library effect styles
gotchas.md
— layout pitfalls (HUG/FILL interactions, counterAxisAlignItems, sizing order), paint/color issues, page context resets
Error Recovery
Follow the error recovery process from
figma-use
:
STOP
on error — do not retry immediately.
Read the error message carefully
to understand what went wrong.
If the error is unclear, call
get_metadata
or
get_screenshot
to inspect the current file state.
Fix the script
based on the error message.
Retry
the corrected script — this is safe because failed scripts are atomic (nothing is created if a script errors).
Because this skill works incrementally (one section per call), errors are naturally scoped to a single section. Previous sections from successful calls remain intact.
Best Practices
Always search before building.
The design system likely has the component, variable, or style you need. Manual construction and hardcoded values should be the exception, not the rule.
Search broadly.
Try synonyms and partial terms. A "NavigationPill" might be found under "pill", "nav", "tab", or "chip". For variables, search "color", "spacing", "radius", etc.
Prefer design system tokens over hardcoded values.
Use variable bindings for colors, spacing, and radii. Use text styles for typography. Use effect styles for shadows. This keeps the screen linked to the design system.
Prefer component instances over manual builds.
Instances stay linked to the source component and update automatically when the design system evolves.
Work section by section.
Never build more than one major section per
use_figma
call.
Return node IDs from every call.
You'll need them to compose sections and for error recovery.
Validate visually after each section.
Use
get_screenshot
to catch issues early.
Match existing conventions.
If the file already has screens, match their naming, sizing, and layout patterns.