Use this skill alongside figma-use when the task involves translating an application page, view, or multi-section layout into Figma. Triggers: 'write to Figma', 'create in Figma from code', 'push page to Figma', 'take this app/page and build it in Figma', 'create a screen', 'build a landing page in Figma', 'update the Figma screen to match code', 'convert this modal/dialog/drawer/panel to Figma'. This is the preferred workflow skill whenever the user wants to build or update a full page, modal, dialog, drawer, sidebar, panel, form, dashboard, list, table, card, header, message preview, or other composed product UI in Figma from code or a description—even when the user calls that composed UI a component. Discovers design system components, variables, and styles from Code Connect files, existing screens, and library search, then imports them and assembles views incrementally section-by-section using design system tokens instead of hardcoded values.
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Hard deliverable gate: Code-to-design output must recreate the UI as editable text, components, icons, and hierarchy. Treat any screenshot or flattened rendering of the complete UI only as a visual reference: never upload or import it into the deliverable, including as the wrapper fill or a child node, even if it exists in the repository or appears more pixel-accurate. Before completion, inspect the result; if the UI is not primarily represented by editable layers, rebuild it semantically or report failure. Repeated UI is delivered as instances of one main component, not as N copies of a frame.
Use this skill to create or update screens, views, and multi-section UI containers in Figma by reusing the published design system — components, variables, and styles — rather than drawing primitives with hardcoded values. This includes full pages, modals, dialogs, drawers, sidebars, panels, and any composed view with multiple sections. 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.
When available, call create_session with fileKey and description before file reads or edits (create the file first if needed), then pass the returned sessionId unchanged to every call that accepts it, including follow-ups and work across files.
Always include figma-generate-design in the comma-separated skillNames parameter when calling use_figma as part of this skill. If this skill was loaded via an MCP resource, you MUST prefix the name with resource: (e.g. resource:figma-generate-design). This is a logging parameter — it does not affect execution.
Reuse available source, captured, or library images first. If the requested design needs images and suitable assets are unavailable, call Figma's generate_image tool to create them.
Use html_to_figma for initial creation in these cases:
html_to_figma.html_to_figma.If html_to_figma is unavailable, follow the existing workflow below.
fileKey). If they don't have one yet, invoke /figma-create-new-file (or call create_new_file) first and reuse the returned file_key. Both use_figma and generate_figma_design require an existing fileKey.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:
fileKey).generate_figma_design against the same fileKey to capture a pixel-perfect screenshot of the running web app into that file. generate_figma_design always requires fileKey — if the user does not yet have a Figma file, first invoke /figma-create-new-file (or call the create_new_file MCP tool) to get one, and reuse that file_key for both this skill and the capture.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).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 discrete content images used by the app, such as photos, avatars, logos, or icons. A screenshot or flattened rendering of the complete UI is a visual reference, not a content image. 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 these content images are present, their image frames will be left blank.
For non-web apps (iOS, Android, etc.) or when updating existing screens, use the standard workflow below.
Follow these steps in order. Do not skip steps.
Required invariants:
- Complete Code Connect discovery and select the target library before mutating the deliverable. Discovery-only imports and temporary instances are allowed; remove temporary instances immediately.
- Apply each selected asset where it is imported. If an import fails, try the next applicable exact result before building a manual fallback.
Before touching Figma, understand what you're building:
.map()) or that the source defines as a reusable component (<DayCell>, <StatusBadge>, <NavItem>, <MetricCard>, <Button>). For each, note what differs between occurrences: text, optional parts, and visual state. Step 2d turns this into a reuse decision.<img>, <Image>, 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.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-i — REQUIRED: Check Code Connect for needed components. Starting from the component list you built in Step 1, check whether each component has a Code Connect file in the codebase. Code Connect files live next to the component source and are named by platform:
*.figma.ts, *.figma.js*.figma.tsx.kt files containing @FigmaConnect.swift files containing FigmaConnectFor each component you need (e.g., Button, Card, Input), search for its Code Connect file — glob or grep by component name (e.g., **/Button.figma.tsx, **/Card.figma.ts). Only read files that match components you actually need.
From each matching Code Connect file, extract the Figma component URL. Parse fileKey and nodeId from the URL (convert hyphens to colons: 123-456 → 123:456). Then resolve component keys via use_figma:
Example: Code Connect file contains // url=https://figma.com/design/ABC123/File?node-id=609-35535. Parse fileKey = ABC123, nodeId = 609:35535. Run use_figma against the library file (fileKey ABC123, not the target file) to resolve the key:
const node = await figma.getNodeByIdAsync("609:35535");
const set = node?.parent?.type === "COMPONENT_SET" ? node.parent : node;
if (set?.type === "COMPONENT_SET") {
return { assetType: "component_set", componentSetKey: set.key };
}
if (set?.type === "COMPONENT") {
return { assetType: "component", componentKey: set.key };
}
throw new Error("Expected a component or component set");Batch multiple lookups in a single call. Use the returned keys with either importComponentByKeyAsync() or importComponentSetByKeyAsync() depending on each item's assetType in Step 4.
Mark resolved components and continue with unresolved ones.
2a-ii — REQUIRED: Select the library scope, then search and inspect the whole view. Call get_libraries. If the user names one or more libraries, use their exact keys whether they appear in libraries_added_to_file or libraries_available_to_add. Otherwise search across all libraries in libraries_added_to_file together. Pass the selected keys in includeLibraryKeys on every search. Only when unresolved gaps have no suitable match, add specifically relevant libraries from libraries_available_to_add and retry those unresolved queries; do not broaden to unrelated available libraries. In the first batched search_design_system call, include one component query for the requested view or flow alongside queries for unresolved items already known from source or Code Connect inspection. Name the whole-view query by purpose and container (for example, billing settings drawer). Inspect the strongest candidate. If it covers two or more requested sections, select it as a foundation; otherwise continue without a whole-view composite.
2a-iii — Inspect existing screens. Check if the target file already contains screens using the same design system. One read-only use_figma call inventories an existing frame — its component keys, bound variables, and text/effect styles — in a single authoritative pass. Run it once here and reuse the result in 2b and 2c rather than re-walking the frame:
const frame = figma.currentPage
.query('[name="Existing Screen"]')
.first();
if (!frame) {
return {
components: [],
variables: [],
textStyles: [],
effectStyles: [],
};
}
const components = new Map();
frame.query('INSTANCE').each(instance => {
const mc = instance.mainComponent;
if (!mc) return;
const cs = mc.parent?.type === 'COMPONENT_SET' ? mc.parent : null;
const asset = cs ?? mc;
if (asset.key && !components.has(asset.key)) {
components.set(asset.key, {
name: asset.name,
key: asset.key,
isSet: !!cs,
sampleVariant: mc.name,
});
}
});
// Inspect bindings on every descendant node that has them.
const variableIds = new Set(
frame.query('[boundVariables.*]')
.values(['boundVariables'])
.flatMap(({ boundVariables }) =>
Object.values(boundVariables).flat()
)
.map(binding => binding?.id)
.filter(Boolean)
);
async function collectStyles(prop) {
const ids = new Set(
frame.query(`[${prop}]`)
.values([prop])
.map(row => row[prop])
.filter(id => typeof id === 'string' && id.length > 0)
);
const styles = await Promise.all(
[...ids].map(id => figma.getStyleByIdAsync(id))
);
return styles.filter(Boolean).map(({ name, id, key }) => ({
name, id, key,
}));
}
const [variables, textStyles, effectStyles] = await Promise.all([
Promise.all(
[...variableIds].map(id => figma.variables.getVariableByIdAsync(id))
),
collectStyles('textStyleId'),
collectStyles('effectStyleId'),
]);
return {
components: [...components.values()],
variables: variables.filter(Boolean).map(v => ({
name: v.name,
id: v.id,
key: v.key,
type: v.resolvedType,
remote: v.remote,
})),
textStyles,
effectStyles,
};Match components against your unresolved components; Code Connect keys from 2a-i stay authoritative. Mark any newly resolved.
2a-iv — Search only the gaps needed for construction. After Code Connect, whole-view discovery, and existing-screen inspection, search for unresolved components and tokens the design still needs. Batch independent one-intent entries, keep includeLibraryKeys restricted to the selected library scope, and carry selected keys into the build. Do not delay construction to exhaustively search every unresolved role.
// Step 1: Discover available libraries
get_libraries({ fileKey })
// Returns: {
// libraries_added_to_file: [...],
// libraries_available_to_add: [...],
// libraries_available_to_add_next_offset: number | null
// }
// Step 2: Search within a specific library using its libraryKey
search_design_system({ queries: [{ entity: "component", query: "button" }], fileKey, includeLibraryKeys: ["lk-abc123..."] })Org libraries in libraries_available_to_add are paginated (20 per page). When libraries_available_to_add_next_offset is non-null, more org libraries are available — call get_libraries again with offset set to that value to fetch the next page. Community UI kits only appear on the first page. If the user names a specific library you don't see in the current page, page further before giving up.
This is especially useful when the file has many libraries and you want targeted results (e.g. searching only within "iOS 26" or "Material 3" instead of getting matches from every library).
Use one intent per query. search_design_system does not apply OR semantics, so never pack alternatives or synonyms into one string ("Button IconButton icon" matches nothing useful). Use an exact design-system name or component type when known; otherwise search by the asset's UI role, such as "archive action icon" or "primary submit button".
Map every selected key to a section or property. Reuse applicable composite descendants instead of building parallel replacements, and keep nested instances intact unless only an example/template wrapper must be detached.
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.
Example component map with property info:
Component Map:
- Button → key: "abc123", type: COMPONENT_SET
Properties: { "Label#2:0": TEXT, "Has Icon#4:64": BOOLEAN }
- PricingCard → key: "ghi789", type: COMPONENT_SET
Properties: { "Device": VARIANT, "Variant": VARIANT }
Nested "Text Heading" has: { "Text#2104:5": TEXT }
Nested "Button" has: { "Label#2:0": TEXT }Inspect existing screens first. Reuse variables from the 2a-iii inventory; if that step was skipped, run the inventory here when an existing screen is available. For unresolved variables, use search_design_system with queries entries whose entity is "variable".
WARNING: Two different variable discovery methods — do not confuse them.
use_figmawithfigma.variables.getLocalVariableCollectionsAsync()— returns only local variables defined in the current file. If this returns empty, it does not mean no variables exist. Remote/published library variables are invisible to this API.search_design_systemwithentity: "variable"query entries — searches remote and published library variables. Scope every query to the library keys selected in 2a-ii withincludeLibraryKeys.Never conclude "no variables exist" based solely on
getLocalVariableCollectionsAsync()returning empty. Always also runsearch_design_systemwith variable query entries to check for library variables before deciding to create your own.
Query strategy: search_design_system matches against variable names (e.g., "Gray/gray-9", "core/gray/100", "space/400"), not categories. Put multiple short, simple searches in one queries call rather than one compound query. Every array entry must be an { "entity": "variable", "query": "..." } object, never a bare string:
{ "entity": "variable", "query": "gray" }, { "entity": "variable", "query": "red" }, etc.{ "entity": "variable", "query": "background" }, { "entity": "variable", "query": "surface" }, etc.{ "entity": "variable", "query": "space" }, { "entity": "variable", "query": "radius" }, etc.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").
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.
Reuse textStyles and effectStyles from the inventory in 2a-iii or 2b. If no inventory exists, inspect an available screen with the same script. Before hardcoding an unresolved reusable fill/stroke, typography, or effect role, issue a focused search_design_system entry with entity: "style", such as "surface", "border", "heading", "body", "shadow", or "elevation". Batch independent entries, but do not exhaustively search roles covered by a selected component or existing-screen style. Search for any styles still missing.
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.
For each item from the Step 1 reuse inventory, record exactly one decision:
Differences between occurrences become component properties: TEXT for labels, numbers, and dates; BOOLEAN for optional parts; variants only for meaningful visual states. Do not make every label or value a variant. See references/componentization.md.
Reuse decisions:
- Day Cell → create once: set State=Default/Today/Outside; TEXT Date; BOOLEAN Show Event 1..3
- Event Chip → create once: set Color=Blue/Green; TEXT Title
- Button/Primary → reuse: key "abc123"
- Month grid, Header bar → frameMUST use $fig Use $fig for all node creation. See fig-builder.md for API reference.
Use placeholders and do not fill in all sections at once — New nodes with placeholder: true will show up in the tool call result after plan changes are auto-flushed, e.g. { created: { '0:3': 'My Screen', '0:4': 'Header Placeholder' }}
If Step 2 selected a whole-view composite, instance it inside the wrapper with $fig.instance(...) and create placeholders only for the sections it does not cover. Customize its covered descendants in Step 4; never append children directly to an instance.
// --- Size the wrapper based on container type ---
// Full page: { width: 1440, name: "Homepage" }
// Modal/dialog: { width: 640, name: "Settings Modal" }
// Drawer/sidebar: { width: 360, name: "Navigation Drawer" }
// Panel: { width: 400, name: "Details Panel" }
// Adapt width to match the source code's actual dimensions.
$fig.autoLayout(
{
name: 'VIEW_NAME',
layoutMode: 'VERTICAL',
primaryAxisAlignItems: 'CENTER',
counterAxisAlignItems: 'CENTER',
width: WIDTH,
},
// Create placeholder sections for the screen
[
$fig.autoLayout({ name: 'Header', layoutSizingHorizontal: 'FILL', placeholder: true }),
$fig.autoLayout({ name: 'Content', layoutSizingHorizontal: 'FILL', placeholder: true }),
$fig.autoLayout({ name: 'Footer', layoutSizingHorizontal: 'FILL', placeholder: true }),
]
)In a dedicated use_figma call, build every "create once" component or set from 2d, outside the view: in the file's existing component page or area if it has one, otherwise in a dedicated components area off to the side of the wrapper. return their node IDs. Create shared parts first: a component nested in another (Event Chip inside Day Cell) must exist before the parent. Section calls in Step 4 then place instances by ID. A plan handle does not survive between calls, so the returned node ID is how later calls reuse the component.
Follow the complete, tested recipe in references/componentization.md. It covers variants plus TEXT and BOOLEAN properties, instances with different values, nested overrides, and differing child counts without detaching.
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 section placeholder by ID and replace it with a real section.
const header = $fig.get("HEADER_PLACEHOLDER_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");
const shadowStyle = await figma.importStyleByKeyAsync("SHADOW_STYLE_KEY");
// Replace the placeholder with a real section frame with variable bindings (not hardcoded values)
header.replace(
$fig.autoLayout(
{
name: 'Header',
layoutSizingHorizontal: 'FILL', // Set to FILL because auto-layout frames default to HUG
paddingLeft: spacingVar,
paddingRight: spacingVar,
fills: [{ type: 'SOLID', color: bgColorVar }],
effects: shadowStyle,
},
[
$fig.instance(primaryButton)
]
)
).screenshot()Validate the section with .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.
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:
// Use the type-indexed criteria for the type filter, then narrow by name.
const nestedHeading = cardInstance
.findAllWithCriteria({ types: ["INSTANCE"] })
.find(n => 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.
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, <Button size="small">Register</Button> 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.
| Build manually | Import from design system |
|---|---|
| Wrapper frame | Components: buttons, cards, inputs, nav, etc. |
| Section container frames | Variables: colors (fills, strokes), spacing (padding, gap), radii |
| Layout grids (rows, columns) | Text styles: heading, body, caption, etc. |
| Effect styles: 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.
Componentization is part of the default workflow, not an optional follow-up. Produce a componentized structure on the first pass; do not emit a flat tree of one-off frames and wait for a second "now make it componentized" prompt.
$fig.component(...) and place instances with $fig.instance(...), instead of hand-building N near-identical frames. One source component maps to one Figma main component.Place instances of the Step 3b components for every repeated element, with per-item props. Do not hand-build a copy of an element you already made a component:
$fig.get("GRID_PLACEHOLDER_ID").replace(
$fig.autoLayout({ name: 'Metrics', layoutSizingHorizontal: 'FILL', itemSpacing: 16 },
METRICS.map((m) => $fig.instance("METRIC_CARD_SET_ID", {
name: m.label,
props: { Trend: m.up ? 'Up' : 'Down', Label: m.label, Value: m.value },
})),
),
).screenshot()See references/componentization.md for the build-once-place-instances pattern and code.
Icons are the main exception to the build-manually-vs-import split above. If the design system exposes an icon as a component, instance it (a single INSTANCE_SWAP property, not a variant per icon). Otherwise — most commonly when grabbing an icon from the codebase to place or replace it in Figma — import the icon's SVG source directly as a vector node. This is the primary, default path for icons; do not redraw them.
<svg>, the imported .svg asset, or the icon-library entry — and pass that exact SVG string. Prefer the codebase's own SVG over hand-authoring one.$fig.svg(svgString, opts?), which parses the SVG into a vector node tree. The SVG string must include a viewBox plus explicit width/height (e.g. <svg width="24" height="24" viewBox="0 0 24 24" ...>); without them it falls back to the viewBox size, which is often smaller than the slot — the usual cause of "the icon didn't size properly." (The imperative figma.createNodeFromSvg(svgString) also works and returns a FrameNode you can .resize(size, size).)width/height in the SVG string. Append it like any other plan node: as a child in the parent's children, or slot.svg(svgString, opts).use_figma context and produces broken, mis-rotated icons (a chevron collapses into a blob, an arrowhead detaches from its shaft). Importing the SVG is both more reliable and more editable.// Import a codebase icon SVG as a vector, sized to a 24px slot
const chevron = $fig.svg(
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
'<path d="m9 18 6-6-6-6" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
{ name: 'icon/chevron-right' }
);
// include as a child when building a section — e.g. $fig.autoLayout({...}, [ label, chevron ])
// or append onto an existing plan node — e.g. row.svg('<svg .../>', { name: 'icon/chevron-right' })Codebase SVGs usually use currentColor (e.g. stroke="currentColor" / fill="currentColor"), which createNodeFromSvg imports as black — it does not inherit the parent's color. Set the intended color after import: substitute the literal color into the SVG string before importing, or bind the imported vector fills/strokes to a design-system color variable with setBoundVariableForPaint (same as any paint). To turn an imported SVG into a reusable icon component (for INSTANCE_SWAP), see figma-generate-library → Creating Icon Components and the INSTANCE_SWAP pattern.
Before visual validation, use use_figma to read back the wrapper and return its total descendant count, counts for every descendant node type present, and the ID, name, node type, and dimensions of every image-filled node. These counts are evidence, not a numeric pass threshold; do not infer editability from a screenshot or accept token editable nodes layered over a complete-UI raster. If a screenshot or flattened rendering of the complete UI is present anywhere inside the wrapper, remove it and rebuild the UI semantically or report failure; do not claim completion.
After composing all sections, take one full-view composition screenshot of the wrapper frame and compare against the source. If it reveals a meaningful visual defect, apply targeted use_figma fixes — don't rebuild the entire view — then take one post-fix screenshot. The most recent passing screenshot is the final check: do not take an additional unchanged "final" shot, and do not screenshot every section individually.
Before visual fixes, compare the wrapper's instance, style, and bound-variable keys with the Step 2 map. Every applicable selected asset should appear in the output; replace manual equivalents and raw values before finishing.
Inspect the composition screenshot for:
generate_figma_design capture before considering generate_image to generate placeholders (see "Transfer images from the generate_figma_design capture" below)Screenshots cannot show whether repeated items are instances. Run the read-only structural check in references/componentization.md on the wrapper. Every inventory item marked "reuse" or "create once" must appear as instances. There must be no unused main components, no main components inside the view, and no repeated item left as duplicated frames. Fix what it reports before finishing.
You MUST explicitly assert that rendered text uses the product font(s) identified in Step 1, and treat any mismatch as a failed validation. Do not assume a successfully loaded font is the correct font: loading Inter when the product uses SF Pro is a failure even if no errors occur. If you have a source reference (the running web app, a design mock, or the generate_figma_design capture), you MUST also compare rendered screenshots — a near-miss style within the right family can pass a family check but still look wrong.
See references/discover-product-font.md for the read-back script (which separates free-standing text you fix from design-system-governed text you flag) and the screenshot-comparison steps.
If you ran generate_figma_design in parallel, transfer only discrete content assets used by the app into your design system output. Never upload or transfer the capture root or any screenshot or flattened rendering of the complete UI.
type === "IMAGE":
const capture = await figma.getNodeByIdAsync("CAPTURE_NODE_ID");
const imageNodes = capture.findAll(() => true).flatMap(n => {
if (!Array.isArray(n.fills)) return [];
const imageFill = n.fills.find(f => f.type === "IMAGE");
return imageFill ? [{ name: n.name, id: n.id, imageHash: imageFill.imageHash }] : [];
});
return imageNodes;targetFrame.fills = [{ type: "IMAGE", imageHash: "hash_from_capture", scaleMode: "FILL" }];generate_figma_design capture output after all images are transferred.After generation, inform the user that Figma AI credits were used.
If Figma AI is unavailable or its credit limit is reached, ask before using an alternative image-generation provider. Upload only these alternative-provider images with upload_assets.
When updating rather than creating from scratch:
get_metadata to inspect the existing screen structure.// Example: Swap a button variant in an existing screen.
// Batch the node lookup and component-set import in parallel — they are
// independent and awaiting them sequentially serializes two IPC round-trips.
const [existingButton, buttonSet] = await Promise.all([
figma.getNodeByIdAsync("EXISTING_BUTTON_INSTANCE_ID"),
figma.importComponentSetByKeyAsync("BUTTON_SET_KEY"),
]);
if (existingButton && existingButton.type === "INSTANCE") {
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] };For detailed API patterns and gotchas, load these from the figma-use references as needed:
Follow figma-use error recovery:
safeToRetryWithoutCanvasRead is true, fix the error and retry.false, read the canvas, determine what changed, then make changes.Because this skill works in retry-safe construction phases, errors are naturally scoped to the current phase. Content from previous successful calls remains intact.
{ entity, query } entries in one queries call, never combined into one string—a "NavigationPill" might be found under "pill", "nav", "tab", or "chip", so pass those as four component entries. For variables, use entity: "variable" with queries like "color", "spacing", and "radius". Do this only for unresolved construction gaps, and stop once a suitable asset is found.figma.createFrame(), figma.createText(), etc. Use $fig.autoLayout(), $fig.text(), $fig.rectangle(), etc.use_figma call..screenshot() on nodes to catch issues early.figma/mcp-server-guide
19be8d5
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.