Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Figma skill for writing directly to the Figma canvas through the MCP server and Plugin API.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
common-patterns.md
1# Common Patterns23> Part of the [use_figma skill](../SKILL.md). Working code examples for frequently used operations.45## Contents67- Basic Script Structure8- Create a Styled Shape9- Create a Text Node10- Create Frame with Auto-Layout11- Create Variable Collections and Bindings12- Create Components and Import by Key13- Component Sets with Variable Modes14- Multi-Step Large ComponentSet Pattern15- Read Existing Nodes and Return Data161718## Basic Script Structure1920```js21const createdNodeIds = []22const mutatedNodeIds = []2324// Your code here — track every node you create or mutate25// createdNodeIds.push(newNode.id)26// mutatedNodeIds.push(existingNode.id)2728return {29success: true,30createdNodeIds,31mutatedNodeIds,32// Plus any other useful data for subsequent calls33count: createdNodeIds.length34}35```3637## Create a Styled Shape3839```js40// Find clear space to the right of existing content41const page = figma.currentPage42let maxX = 043for (const child of page.children) {44maxX = Math.max(maxX, child.x + child.width)45}4647const rect = figma.createRectangle()48rect.name = "Blue Box"49rect.resize(200, 100)50rect.fills = [{ type: 'SOLID', color: { r: 0.047, g: 0.549, b: 0.914 } }]51rect.cornerRadius = 852rect.x = maxX + 100 // offset from existing content53rect.y = 054figma.currentPage.appendChild(rect)55return { nodeId: rect.id }56```5758## Create a Text Node5960```js61// Find clear space to the right of existing content62const page = figma.currentPage63let maxX = 064for (const child of page.children) {65maxX = Math.max(maxX, child.x + child.width)66}6768await figma.loadFontAsync({ family: "Inter", style: "Regular" })69const text = figma.createText()70text.characters = "Hello World"71text.fontSize = 1672text.fills = [{ type: 'SOLID', color: { r: 0, g: 0, b: 0 } }]73text.textAutoResize = 'WIDTH_AND_HEIGHT'74text.x = maxX + 10075text.y = 076figma.currentPage.appendChild(text)77return { nodeId: text.id }78```7980## Create Frame with Auto-Layout8182```js83// Find clear space to the right of existing content84const page = figma.currentPage85let maxX = 086for (const child of page.children) {87maxX = Math.max(maxX, child.x + child.width)88}8990const frame = figma.createFrame()91frame.name = "Card"92frame.layoutMode = 'VERTICAL'93frame.primaryAxisAlignItems = 'MIN'94frame.counterAxisAlignItems = 'MIN'95frame.paddingLeft = 1696frame.paddingRight = 1697frame.paddingTop = 1298frame.paddingBottom = 1299frame.itemSpacing = 8100frame.layoutSizingHorizontal = 'HUG'101frame.layoutSizingVertical = 'HUG'102frame.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }]103frame.cornerRadius = 8104frame.x = maxX + 100105frame.y = 0106figma.currentPage.appendChild(frame)107return { nodeId: frame.id }108```109110## Create Variable Collection with Multiple Modes111112```js113const collection = figma.variables.createVariableCollection("Theme/Colors")114// Rename the default mode115collection.renameMode(collection.modes[0].modeId, "Light")116const darkModeId = collection.addMode("Dark")117const lightModeId = collection.modes[0].modeId118119const bgVar = figma.variables.createVariable("bg", collection, "COLOR")120bgVar.setValueForMode(lightModeId, { r: 1, g: 1, b: 1, a: 1 })121bgVar.setValueForMode(darkModeId, { r: 0.1, g: 0.1, b: 0.1, a: 1 })122123const textVar = figma.variables.createVariable("text", collection, "COLOR")124textVar.setValueForMode(lightModeId, { r: 0, g: 0, b: 0, a: 1 })125textVar.setValueForMode(darkModeId, { r: 1, g: 1, b: 1, a: 1 })126127return {128collectionId: collection.id,129lightModeId,130darkModeId,131bgVarId: bgVar.id,132textVarId: textVar.id133}134```135136## Bind Color Variable to a Fill137138```js139const variable = await figma.variables.getVariableByIdAsync("VariableID:1:2")140const rect = figma.createRectangle()141const basePaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }142143// setBoundVariableForPaint returns a NEW paint — capture it!144const boundPaint = figma.variables.setBoundVariableForPaint(basePaint, "color", variable)145rect.fills = [boundPaint]146147return { nodeId: rect.id }148```149150## Create Component Variants with Component Properties151152Component properties (TEXT, BOOLEAN, INSTANCE_SWAP) MUST be added inside the per-variant loop, BEFORE `combineAsVariants`. The component set inherits them from its children.153154```js155await figma.loadFontAsync({ family: "Inter", style: "Regular" })156157// Assume defaultIconComp is an existing icon component (discovered earlier)158const defaultIconComp = figma.getNodeById('ICON_COMPONENT_ID')159160const components = []161const variants = ["primary", "secondary"]162163for (const variant of variants) {164const comp = figma.createComponent()165comp.name = `variant=${variant}`166comp.layoutMode = 'HORIZONTAL'167comp.primaryAxisAlignItems = 'CENTER'168comp.counterAxisAlignItems = 'CENTER'169comp.paddingLeft = 12170comp.paddingRight = 12171comp.paddingTop = 8172comp.paddingBottom = 8173comp.layoutSizingHorizontal = 'HUG'174comp.layoutSizingVertical = 'HUG'175comp.cornerRadius = 6176comp.itemSpacing = 8177178// TEXT property — label179const labelKey = comp.addComponentProperty('Label', 'TEXT', 'Button')180const label = figma.createText()181label.characters = "Button"182label.fontSize = 14183comp.appendChild(label)184label.componentPropertyReferences = { characters: labelKey }185186// BOOLEAN + INSTANCE_SWAP — icon slot187const showIconKey = comp.addComponentProperty('Show Icon', 'BOOLEAN', false)188const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', defaultIconComp.id)189const iconInstance = defaultIconComp.createInstance()190comp.insertChild(0, iconInstance) // icon before label191iconInstance.componentPropertyReferences = {192visible: showIconKey,193mainComponent: iconSlotKey194}195196components.push(comp)197}198199const componentSet = figma.combineAsVariants(components, figma.currentPage)200componentSet.name = "Button"201202// Layout variants in a row after combining (they stack at 0,0 by default)203const colW = 140204componentSet.children.forEach((child, i) => {205child.x = i * colW206child.y = 0207})208// Resize from actual child bounds — formula-based sizing is error-prone209let maxX = 0, maxY = 0210for (const c of componentSet.children) {211maxX = Math.max(maxX, c.x + c.width)212maxY = Math.max(maxY, c.y + c.height)213}214componentSet.resizeWithoutConstraints(maxX + 40, maxY + 40)215216return {217componentSetId: componentSet.id,218componentIds: components.map(c => c.id)219}220```221222## Import a Component by Key (Team Libraries)223224`importComponentByKeyAsync` and `importComponentSetByKeyAsync` import components from **team libraries** (not the same file you're working in). For components in the current file, use `figma.getNodeByIdAsync()` or `findOne()`/`findAll()` to locate them directly.225226```js227// Import a single published component by key228const comp = await figma.importComponentByKeyAsync("COMPONENT_KEY")229const instance = comp.createInstance()230instance.x = 40231instance.y = 40232figma.currentPage.appendChild(instance)233234// Import a published component set by key and select a variant235const compSet = await figma.importComponentSetByKeyAsync("COMPONENT_SET_KEY")236const variant =237compSet.children.find((c) =>238c.type === "COMPONENT" && c.name.includes("size=md")239) || compSet.defaultVariant240241const variantInstance = variant.createInstance()242variantInstance.x = 240243variantInstance.y = 40244figma.currentPage.appendChild(variantInstance)245246return {247componentId: comp.id,248componentSetId: compSet.id,249placedInstanceIds: [instance.id, variantInstance.id]250}251```252253## Component Set with Variable Modes (Full Pattern)254255```js256await figma.loadFontAsync({ family: "Inter", style: "Medium" })257258// 1. Create color collection with modes per variant259const colors = figma.variables.createVariableCollection("Component/Colors")260colors.renameMode(colors.modes[0].modeId, "primary")261const primaryMode = colors.modes[0].modeId262const secondaryMode = colors.addMode("secondary")263264const bgVar = figma.variables.createVariable("bg", colors, "COLOR")265bgVar.setValueForMode(primaryMode, { r: 0, g: 0.4, b: 0.9, a: 1 })266bgVar.setValueForMode(secondaryMode, { r: 0, g: 0, b: 0, a: 0 })267268const textVar = figma.variables.createVariable("text-color", colors, "COLOR")269textVar.setValueForMode(primaryMode, { r: 1, g: 1, b: 1, a: 1 })270textVar.setValueForMode(secondaryMode, { r: 0.1, g: 0.1, b: 0.1, a: 1 })271272// 2. Create components with variable bindings273const modeMap = { primary: primaryMode, secondary: secondaryMode }274const components = []275276for (const [variantName, modeId] of Object.entries(modeMap)) {277const comp = figma.createComponent()278comp.name = "variant=" + variantName279comp.layoutMode = "HORIZONTAL"280comp.primaryAxisAlignItems = "CENTER"281comp.counterAxisAlignItems = "CENTER"282comp.paddingLeft = 12; comp.paddingRight = 12283comp.layoutSizingHorizontal = "HUG"284comp.layoutSizingVertical = "HUG"285comp.cornerRadius = 6286287// Bind background fill to variable288const bgPaint = figma.variables.setBoundVariableForPaint(289{ type: "SOLID", color: { r: 0, g: 0, b: 0 } }, "color", bgVar290)291comp.fills = [bgPaint]292293// Add text with bound color294const label = figma.createText()295label.fontName = { family: "Inter", style: "Medium" }296label.characters = "Button"297label.fontSize = 14298const textPaint = figma.variables.setBoundVariableForPaint(299{ type: "SOLID", color: { r: 0, g: 0, b: 0 } }, "color", textVar300)301label.fills = [textPaint]302comp.appendChild(label)303304// 3. CRITICAL: Set explicit mode so this variant renders correctly305comp.setExplicitVariableModeForCollection(colors, modeId)306307components.push(comp)308}309310// 4. Combine into component set311const componentSet = figma.combineAsVariants(components, figma.currentPage)312componentSet.name = "Button"313314return {315componentSetId: componentSet.id,316colorCollectionId: colors.id317}318```319320## Large ComponentSet with Variable Modes (Multi-Step Pattern)321322For component sets with many variants (50+), split into multiple `use_figma` calls:323324**Call 1: Create variable collections and return IDs**325326```js327// Hex-to-0-1 helper328const hex = (h) => {329if (!h) return { r: 0, g: 0, b: 0, a: 0 }; // transparent330return {331r: parseInt(h.slice(1,3), 16) / 255,332g: parseInt(h.slice(3,5), 16) / 255,333b: parseInt(h.slice(5,7), 16) / 255,334a: 1335};336};337338const coll = figma.variables.createVariableCollection("MyComponent/Colors");339coll.renameMode(coll.modes[0].modeId, "mode1");340const mode2Id = coll.addMode("mode2");341342// Create variables from data map343const colorData = { "bg/default": ["#0B6BCB", "#636B74"], /* ... */ };344const modeOrder = ["mode1", "mode2"];345const modeIds = { mode1: coll.modes[0].modeId, mode2: mode2Id };346const varIds = {};347348for (const [name, values] of Object.entries(colorData)) {349const v = figma.variables.createVariable(name, coll, "COLOR");350values.forEach((hex_val, i) => {351v.setValueForMode(modeIds[modeOrder[i]], hex_val ? hex(hex_val) : { r:0, g:0, b:0, a:0 });352});353varIds[name] = v.id;354}355356// Return ALL IDs — needed by subsequent calls357return { collId: coll.id, modeIds, varIds };358```359360**Call 2: Create components using stored IDs, combine and layout**361362```js363await figma.loadFontAsync({ family: "Inter", style: "Semi Bold" });364365// Paste IDs from Call 1 as literals366const collId = "VariableCollectionId:X:Y";367const modeIds = { mode1: "X:0", mode2: "X:1" };368const varIds = { /* ... from Call 1 ... */ };369370const getVar = async (id) => await figma.variables.getVariableByIdAsync(id);371const bindColor = async (varId) => figma.variables.setBoundVariableForPaint(372{ type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', await getVar(varId)373);374const collection = await figma.variables.getVariableCollectionByIdAsync(collId);375376const components = [];377for (const mode of ["mode1", "mode2"]) {378for (const state of ["default", "hover"]) {379const comp = figma.createComponent();380comp.name = `mode=${mode}, state=${state}`;381comp.layoutMode = 'HORIZONTAL';382comp.primaryAxisAlignItems = 'CENTER';383comp.counterAxisAlignItems = 'CENTER';384comp.layoutSizingHorizontal = 'HUG';385comp.layoutSizingVertical = 'HUG';386comp.fills = [await bindColor(varIds[`bg/${state}`])];387comp.setExplicitVariableModeForCollection(collection, modeIds[mode]);388// ... add text children ...389components.push(comp);390}391}392393// Combine — all children stack at (0,0)!394const cs = figma.combineAsVariants(components, figma.currentPage);395cs.name = "MyComponent";396397// CRITICAL: layout variants in a structured grid mapped to variant axes.398const stateOrder = ["default", "hover"];399const modeOrder2 = ["mode1", "mode2"];400const colW = 140, rowH = 56;401402for (const child of cs.children) {403const props = Object.fromEntries(404child.name.split(', ').map(p => p.split('='))405);406const col = stateOrder.indexOf(props.state);407const row = modeOrder2.indexOf(props.mode);408child.x = col * colW;409child.y = row * rowH;410}411// Resize from actual child bounds412let maxX = 0, maxY = 0;413for (const child of cs.children) {414maxX = Math.max(maxX, child.x + child.width);415maxY = Math.max(maxY, child.y + child.height);416}417cs.resizeWithoutConstraints(maxX + 40, maxY + 40);418419// Wrap in section420const section = figma.createSection();421section.name = "MyComponent Section";422section.appendChild(cs);423section.resizeWithoutConstraints(cs.width + 200, cs.height + 200);424425return { csId: cs.id, count: components.length };426```427428## Read Existing Nodes and Return Data429430```js431const page = figma.currentPage432const nodes = page.findAll(n => n.type === 'FRAME')433const data = nodes.map(n => ({434id: n.id,435name: n.name,436width: n.width,437height: n.height,438childCount: n.children?.length || 0439}))440return { frames: data }441```442