--- title: "Figma Code Connect — Skill Workflow (.figma.ts templates)" aliases: [figma-code-connect-skill, figma-ts-templates, code-connect-workflow] tags: [figma, code-connect, mcp, skill, typescript, design-to-code] sources: [raw/Skill Code Connect Developer Docs.md] created: 2026-05-15 updated: 2026-05-15 --- # Figma Code Connect — Skill Workflow Step-by-step process for creating `.figma.ts` template files that map Figma components to code snippets using the Figma MCP server skill. > Covers **template files only** (`.figma.ts` using MCP tools). Parser-based files (`.figma.tsx` with `figma.connect()` via CLI) are a separate approach. ## Prerequisites - Figma MCP tools available (e.g. `get_code_connect_suggestions`) — verify before starting - Component published to a Figma team library (unpublished = stop) - Organization or Enterprise plan (not Free/Pro) - Figma URL must include `node-id` query param - Add `@figma/code-connect/figma-types` to `tsconfig.json`: ```json { "compilerOptions": { "types": ["@figma/code-connect/figma-types"] } } ``` ## 6-Step Workflow ### Step 1 — Parse the Figma URL Extract `fileKey` and `nodeId`: | URL format | fileKey | nodeId | |-----------|---------|--------| | `figma.com/design/:fileKey/:name?node-id=X-Y` | `:fileKey` | `X-Y` → `X:Y` | | `figma.com/file/:fileKey/:name?node-id=X-Y` | `:fileKey` | `X-Y` → `X:Y` | | Branch URL with `:branchKey` | use `:branchKey` | from `node-id` param | **Always convert hyphens to colons in nodeId**: `1234-5678` → `1234:5678` ### Step 2 — Discover Unmapped Components Call `get_code_connect_suggestions` with `fileKey`, `nodeId`, `excludeMappingPrompt: true`. - **"No published components found"** → tell user to publish first, stop - **"All already connected"** → inform user, stop - **Normal response** → extract `mainComponentNodeId` per component; use these (not the original URL node) for all subsequent steps. Repeat Steps 3–6 for each component. ### Step 3 — Fetch Component Properties Call `get_context_for_code_connect` with `fileKey`, resolved `nodeId`, `clientFrameworks`, `clientLanguages`. Property types returned: | Type | Description | |------|-------------| | TEXT | Text content (labels, placeholders) | | BOOLEAN | Toggle (show/hide, disabled) | | VARIANT | Enum options (size, state) | | INSTANCE_SWAP | Swappable nested instance (icon) | | SLOT | Freeform content region | ### Step 4 — Identify the Code Component 1. Check `figma.config.json` for `paths`/`importPaths` 2. Search codebase in `src/components/`, `components/`, `lib/ui/`, `app/components/` 3. Compare props interface vs Figma properties from Step 3 4. **Confirm with user** before writing the template ### Step 5 — Create the Template File **File location:** alongside existing `.figma.ts`/`.figma.tsx` files, named `ComponentName.figma.ts` #### Template structure ```ts // url=https://www.figma.com/file/{fileKey}/{fileName}?node-id={nodeId} // source={path to code component} // component={component name} import figma from 'figma' const instance = figma.selectedInstance // property extractions... export default { example: figma.code``, imports: ['import { Component } from "..."'], id: 'component-name', metadata: { nestable: true, props: {} } } ``` #### Property mapping methods | Figma Type | Method | Notes | |-----------|--------|-------| | TEXT | `instance.getString('Name')` | Returns string | | BOOLEAN | `instance.getBoolean('Name', { true: ..., false: ... })` | Mapping optional | | VARIANT | `instance.getEnum('Name', { 'FigmaVal': 'codeVal' })` | Must map ALL values | | INSTANCE_SWAP | `instance.getInstanceSwap('Name')` | Returns `InstanceHandle \| null` | | SLOT | `instance.getSlot('Name')` | Returns `ResultSection[] \| undefined` | | Child layer | `instance.findInstance('LayerName')` | No component property | | Text layer | `instance.findText('LayerName').textContent` | Named text layer | #### VARIANT exhaustive mapping (critical) Every value from `get_context_for_code_connect` **must** appear in `getEnum`. Unmapped value → silent `undefined`: ```ts // Correct — all 4 values mapped const status = instance.getEnum('Status', { 'Success': 'success', 'Error': 'error', 'Warning': 'warning', 'Info': 'info', }) ``` #### Interpolation rules | Value type | Wrapping | |-----------|----------| | String (`getString`, `getEnum`, `textContent`) | Quotes: `variant="${variant}"` | | Instance snippet (`executeTemplate().example`) | Braces: `icon={${iconCode}}` | | Slot sections (`getSlot()`) | Directly in template: `` `${content}` `` | | Boolean bare prop | Conditional: `${disabled ? 'disabled' : ''}` | #### Instance swap pattern ```ts const icon = instance.getInstanceSwap('Icon') let iconCode if (icon && icon.type === 'INSTANCE') { // type check required — findInstance returns ErrorHandle on failure iconCode = icon.executeTemplate().example } ``` #### SelectorOptions for `findInstance`/`findText` ```ts // Target inside a nested instance (stop at boundary by default) instance.findInstance('child', { traverseInstances: true }) // Disambiguate duplicate layer names instance.findInstance('child', { traverseInstances: true, path: ['ParentLayer'] }) ``` ### Step 6 — Validate Read back the file and check: - Every Figma property from Step 3 is covered - All emitted attributes exist in the code component's `Props` interface (never invent props) - No hardcoded children — INSTANCE_SWAP and slots use dynamic APIs - INSTANCE_SWAP uses `getInstanceSwap()`, not `getSlot()`; SLOT uses `getSlot()`, not `getInstanceSwap()` - `type === 'INSTANCE'` check present before every `executeTemplate()` call ## Key Takeaways - **6 steps**: parse URL → discover unmapped → fetch properties → identify code component → write template → validate - **VARIANT coverage is mandatory** — every enum value must be in the mapping; missing = silent `undefined` - **Never string-concatenate `ResultSection[]`** — interpolate inside `` figma.code`...` `` tagged templates - **`hasCodeConnect()` guard is wrong** — always call `executeTemplate()` directly after `type === 'INSTANCE'` check - **`getSlot()` ≠ `getInstanceSwap()`** — SLOT type uses `getSlot()`, INSTANCE_SWAP uses `getInstanceSwap()`; they are not interchangeable - **Never hardcode child content** — always resolve dynamically via `executeTemplate()`, omit if no Code Connect exists - **`findInstance()` returns ErrorHandle (truthy) on failure**, not null — always add `type === 'INSTANCE'` guard - **Confirm code component match with user** before writing the template (Step 4) ## Quick API Reference ### `instance.*` methods | Method | Returns | |--------|---------| | `getString(prop)` | `string` | | `getBoolean(prop, mapping?)` | `boolean \| any` | | `getEnum(prop, mapping)` | `any` | | `getInstanceSwap(prop)` | `InstanceHandle \| null` | | `getSlot(prop)` | `ResultSection[] \| undefined` | | `findInstance(name, opts?)` | `InstanceHandle \| ErrorHandle` | | `findText(name, opts?)` | `TextHandle \| ErrorHandle` | | `findConnectedInstance(id, opts?)` | `InstanceHandle \| ErrorHandle` | | `findConnectedInstances(fn, opts?)` | `InstanceHandle[]` | | `findLayers(fn, opts?)` | `(InstanceHandle \| TextHandle)[]` | ### `InstanceHandle` methods | Method | Returns | |--------|---------| | `executeTemplate()` | `{ example: ResultSection[], metadata: Metadata }` | | `hasCodeConnect()` | `boolean` (avoid as a guard — see pitfalls) | | `codeConnectId()` | `string \| null` | ## Related Articles - [[wiki/claude-code/figma-mcp-code-connect|figma-mcp-code-connect]] — higher-level overview: CodeConnectSnippet wrapper, CLI vs MCP mappings - [[wiki/claude-code/figma-mcp-skills|figma-mcp-skills]] — all 8 Figma MCP skills including `figma-code-connect-components` - [[wiki/claude-code/figma-mcp-setup|figma-mcp-setup]] — enable Figma MCP server in Claude Code - [[wiki/claude-code/figma-skill-build-screens|figma-skill-build-screens]] — building/updating Figma screens from design system - [[wiki/claude-code/figma-mcp-guide|figma-mcp-guide]] — Figma MCP server overview and capabilities ## Sources - `raw/Skill Code Connect Developer Docs.md` — Figma Developer Docs, Code Connect skill reference