---
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