figma-code-agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +133 -0
  2. package/bin/install.js +328 -0
  3. package/knowledge/README.md +62 -0
  4. package/knowledge/css-strategy.md +973 -0
  5. package/knowledge/design-to-code-assets.md +855 -0
  6. package/knowledge/design-to-code-layout.md +929 -0
  7. package/knowledge/design-to-code-semantic.md +1085 -0
  8. package/knowledge/design-to-code-typography.md +1003 -0
  9. package/knowledge/design-to-code-visual.md +1145 -0
  10. package/knowledge/design-tokens-variables.md +1261 -0
  11. package/knowledge/design-tokens.md +960 -0
  12. package/knowledge/figma-api-devmode.md +894 -0
  13. package/knowledge/figma-api-plugin.md +920 -0
  14. package/knowledge/figma-api-rest.md +742 -0
  15. package/knowledge/figma-api-variables.md +848 -0
  16. package/knowledge/figma-api-webhooks.md +876 -0
  17. package/knowledge/payload-blocks.md +1184 -0
  18. package/knowledge/payload-figma-mapping.md +1210 -0
  19. package/knowledge/payload-visual-builder.md +1004 -0
  20. package/knowledge/plugin-architecture.md +1176 -0
  21. package/knowledge/plugin-best-practices.md +1206 -0
  22. package/knowledge/plugin-codegen.md +1313 -0
  23. package/package.json +31 -0
  24. package/skills/README.md +103 -0
  25. package/skills/audit-plugin/SKILL.md +244 -0
  26. package/skills/build-codegen-plugin/SKILL.md +279 -0
  27. package/skills/build-importer/SKILL.md +320 -0
  28. package/skills/build-plugin/SKILL.md +199 -0
  29. package/skills/build-token-pipeline/SKILL.md +363 -0
  30. package/skills/ref-html/SKILL.md +290 -0
  31. package/skills/ref-layout/SKILL.md +150 -0
  32. package/skills/ref-payload-block/SKILL.md +415 -0
  33. package/skills/ref-react/SKILL.md +222 -0
  34. package/skills/ref-tokens/SKILL.md +347 -0
@@ -0,0 +1,894 @@
1
+ # Figma Dev Mode & Codegen Plugin Reference
2
+
3
+ ## Purpose
4
+
5
+ Authoritative reference for Figma Dev Mode, the codegen plugin system, and the Dev Resources REST API. Covers the Dev Mode inspect experience, codegen plugin manifests (with correct `editorType: ["dev"]`), the `figma.codegen` API, code generation patterns, and the Dev Resources CRUD endpoints for linking design components to code.
6
+
7
+ ## When to Use
8
+
9
+ Reference this module when you need to:
10
+
11
+ - Build a codegen plugin that generates code in Dev Mode's Inspect panel
12
+ - Configure a `manifest.json` for a Dev Mode codegen plugin
13
+ - Implement the `figma.codegen.on('generate', ...)` callback
14
+ - Use the codegen preferences system (unit, select, action)
15
+ - Create, read, update, or delete Dev Resources via the REST API
16
+ - Link Figma components to code repositories or documentation
17
+ - Understand how Dev Mode relates to the standard Figma editing experience
18
+
19
+ ---
20
+
21
+ ## Content
22
+
23
+ ### Dev Mode Overview
24
+
25
+ Dev Mode is Figma's dedicated developer experience that transforms the Inspect panel into a developer-focused workspace. It surfaces design specs, code snippets, annotations, and developer resources alongside the visual design.
26
+
27
+ #### Key Concepts
28
+
29
+ | Concept | Description |
30
+ |---------|-------------|
31
+ | **Inspect panel** | The right-side panel in Dev Mode showing properties, code, and resources for the selected node |
32
+ | **Code snippets** | Built-in code generation (CSS, iOS, Android) plus custom snippets from codegen plugins |
33
+ | **Dev Resources** | Links attached to nodes pointing to code repos, documentation, or other developer references |
34
+ | **Ready for dev** | A status designers can apply to frames/sections indicating they are ready for implementation |
35
+ | **Annotations** | Design notes and specifications attached to nodes, visible in Dev Mode |
36
+ | **Measurements** | Spacing and dimension overlays visible when hovering between elements |
37
+
38
+ #### Relationship to Standard Editing Mode
39
+
40
+ Dev Mode and Design Mode are separate views of the same Figma file:
41
+
42
+ - **Design Mode** — The editing environment where designers create and modify designs. Standard plugins (`editorType: ["figma"]`) run here.
43
+ - **Dev Mode** — The read-only inspection environment for developers. Codegen plugins (`editorType: ["dev"]`) run here. Developers can view properties, copy code, and access dev resources, but cannot modify the design.
44
+
45
+ Plugins can detect which mode they are running in:
46
+
47
+ ```ts
48
+ if (figma.editorType === 'dev') {
49
+ // Running in Dev Mode
50
+ if (figma.mode === 'codegen') {
51
+ // Specifically running as a codegen plugin
52
+ } else if (figma.mode === 'inspect') {
53
+ // Running as an inspect plugin
54
+ }
55
+ }
56
+ ```
57
+
58
+ ---
59
+
60
+ ### Codegen Plugin System
61
+
62
+ Codegen plugins extend Figma's built-in code generation with custom languages and frameworks. They appear in the Dev Mode Inspect panel's language dropdown and generate code snippets when a user selects a node.
63
+
64
+ #### How Codegen Plugins Work
65
+
66
+ 1. The user opens a Figma file in Dev Mode
67
+ 2. The user selects a codegen plugin's language from the Inspect panel dropdown
68
+ 3. When the user selects a node, Figma fires the `generate` event
69
+ 4. The plugin's callback receives the selected node and returns code snippets
70
+ 5. The code appears in the Inspect panel alongside built-in CSS/iOS/Android code
71
+
72
+ > **Key difference from standard plugins:** Codegen plugins do NOT have an iframe UI by default. The generated code appears directly in the Inspect panel. However, they CAN use `figma.showUI()` for advanced scenarios like action preferences (see Preferences section below).
73
+
74
+ ---
75
+
76
+ ### Codegen Plugin Manifest
77
+
78
+ Codegen plugin manifests use `editorType: ["dev"]` with `capabilities: ["codegen"]`. This is fundamentally different from standard plugin manifests.
79
+
80
+ > **Critical:** Do NOT use `editorType: ["figma"]` or `editorType: ["figma", "figjam"]` for codegen plugins. The audit specifically identified this as a common error. Codegen plugins MUST use `editorType: ["dev"]`.
81
+
82
+ #### Minimal Codegen Manifest
83
+
84
+ ```json
85
+ {
86
+ "name": "My Codegen Plugin",
87
+ "id": "1234567890",
88
+ "api": "1.0.0",
89
+ "main": "code.js",
90
+ "editorType": ["dev"],
91
+ "documentAccess": "dynamic-page",
92
+ "capabilities": ["codegen"],
93
+ "codegenLanguages": [
94
+ { "label": "React", "value": "react" }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ #### Full Codegen Manifest with Preferences
100
+
101
+ ```json
102
+ {
103
+ "name": "Design-to-Code Generator",
104
+ "id": "1234567890",
105
+ "api": "1.0.0",
106
+ "main": "code.js",
107
+ "editorType": ["dev"],
108
+ "documentAccess": "dynamic-page",
109
+ "capabilities": ["codegen", "vscode"],
110
+ "codegenLanguages": [
111
+ { "label": "React", "value": "react" },
112
+ { "label": "Vue", "value": "vue" },
113
+ { "label": "HTML + CSS", "value": "html-css" }
114
+ ],
115
+ "codegenPreferences": [
116
+ {
117
+ "itemType": "unit",
118
+ "propertyName": "Sizing Unit",
119
+ "scaledUnit": "rem",
120
+ "defaultScaleFactor": 16,
121
+ "includedLanguages": ["react", "html-css"]
122
+ },
123
+ {
124
+ "itemType": "select",
125
+ "propertyName": "CSS Strategy",
126
+ "options": [
127
+ { "label": "CSS Modules", "value": "modules", "isDefault": true },
128
+ { "label": "Tailwind", "value": "tailwind" },
129
+ { "label": "Inline Styles", "value": "inline" }
130
+ ],
131
+ "includedLanguages": ["react"]
132
+ },
133
+ {
134
+ "itemType": "action",
135
+ "propertyName": "Component Mapping",
136
+ "label": "Configure Mappings"
137
+ }
138
+ ],
139
+ "networkAccess": {
140
+ "allowedDomains": ["https://api.example.com"],
141
+ "reasoning": "Fetch component mappings from design system server"
142
+ }
143
+ }
144
+ ```
145
+
146
+ #### Manifest Field Reference (Codegen-Specific)
147
+
148
+ | Field | Type | Required | Description |
149
+ |-------|------|:--------:|-------------|
150
+ | `editorType` | string[] | Yes | Must be `["dev"]` for codegen plugins |
151
+ | `capabilities` | string[] | Yes | Must include `"codegen"`. Can also include `"vscode"` for VS Code extension integration and `"inspect"` for inspect panel plugins |
152
+ | `codegenLanguages` | object[] | Yes | Array of `{ label: string, value: string }` defining supported languages |
153
+ | `codegenPreferences` | object[] | No | Preference items for customizing code output (see Preferences section) |
154
+
155
+ #### codegenLanguages
156
+
157
+ Each entry defines a language/framework option in the Dev Mode dropdown:
158
+
159
+ ```json
160
+ "codegenLanguages": [
161
+ { "label": "React (TSX)", "value": "react-tsx" },
162
+ { "label": "Vue SFC", "value": "vue-sfc" },
163
+ { "label": "HTML + CSS", "value": "html-css" },
164
+ { "label": "Tailwind", "value": "tailwind" },
165
+ { "label": "SwiftUI", "value": "swiftui" }
166
+ ]
167
+ ```
168
+
169
+ - `label` — Display name shown in the dropdown
170
+ - `value` — Identifier passed to the generate callback
171
+
172
+ ---
173
+
174
+ ### Codegen API
175
+
176
+ The `figma.codegen` object provides the API for code generation plugins.
177
+
178
+ #### The Generate Callback
179
+
180
+ The core of every codegen plugin is the `generate` event handler:
181
+
182
+ ```ts
183
+ figma.codegen.on('generate', (event: CodegenEvent): CodegenResult[] => {
184
+ const { node, language } = event;
185
+
186
+ // Generate code based on the selected node and language
187
+ return [
188
+ {
189
+ title: 'JSX',
190
+ code: generateJSX(node),
191
+ language: 'TYPESCRIPT'
192
+ },
193
+ {
194
+ title: 'Styles',
195
+ code: generateCSS(node),
196
+ language: 'CSS'
197
+ }
198
+ ];
199
+ });
200
+ ```
201
+
202
+ #### CodegenEvent
203
+
204
+ ```ts
205
+ interface CodegenEvent {
206
+ /** The currently selected node in Dev Mode */
207
+ node: SceneNode;
208
+
209
+ /** The language value from codegenLanguages in manifest */
210
+ language: string;
211
+ }
212
+ ```
213
+
214
+ #### CodegenResult
215
+
216
+ Each result creates a titled code section in the Inspect panel:
217
+
218
+ ```ts
219
+ interface CodegenResult {
220
+ /** Section title displayed above the code block */
221
+ title: string;
222
+
223
+ /** The generated code string */
224
+ code: string;
225
+
226
+ /** Syntax highlighting language */
227
+ language: 'BASH' | 'CPP' | 'CSS' | 'GO' | 'GRAPHQL' | 'HTML' | 'JAVA'
228
+ | 'JAVASCRIPT' | 'JSON' | 'KOTLIN' | 'PLAINTEXT' | 'PYTHON' | 'RUBY'
229
+ | 'RUST' | 'SASS' | 'SCSS' | 'SWIFT' | 'TYPESCRIPT' | 'XML';
230
+ }
231
+ ```
232
+
233
+ Multiple results are rendered as separate collapsible sections in the Inspect panel. This allows a single generate callback to output HTML, CSS, and JavaScript as separate sections.
234
+
235
+ #### Async Generate Callback
236
+
237
+ The generate callback can return a Promise for async operations:
238
+
239
+ ```ts
240
+ figma.codegen.on('generate', async (event: CodegenEvent): Promise<CodegenResult[]> => {
241
+ const { node, language } = event;
242
+
243
+ // Async operations (e.g., loading fonts, resolving variables)
244
+ if (node.type === 'TEXT') {
245
+ await figma.loadFontAsync(node.fontName as FontName);
246
+ }
247
+
248
+ return [{
249
+ title: 'Component',
250
+ code: await generateComponent(node, language),
251
+ language: 'TYPESCRIPT'
252
+ }];
253
+ });
254
+ ```
255
+
256
+ > **Important:** The generate callback has a **3-second timeout**. If the callback does not resolve within 3 seconds, Figma will cancel it. Keep code generation fast.
257
+
258
+ #### Preferences API
259
+
260
+ Access current preference values in the generate callback:
261
+
262
+ ```ts
263
+ figma.codegen.on('generate', (event) => {
264
+ const prefs = figma.codegen.preferences;
265
+
266
+ // Unit preference: { unit: string, scaleFactor: number }
267
+ const sizingUnit = prefs.unit; // e.g., "rem"
268
+ const scaleFactor = prefs.scaleFactor; // e.g., 16
269
+
270
+ // Select preference: check current value
271
+ const cssStrategy = prefs['CSS Strategy']; // e.g., "modules"
272
+
273
+ // Use preferences in code generation
274
+ const pxValue = node.width;
275
+ const remValue = pxValue / scaleFactor;
276
+ const sizeStr = sizingUnit === 'rem' ? `${remValue}rem` : `${pxValue}px`;
277
+
278
+ return [{ title: 'Styles', code: `width: ${sizeStr};`, language: 'CSS' }];
279
+ });
280
+ ```
281
+
282
+ #### Preference Change Events
283
+
284
+ ```ts
285
+ // Listen for action preference changes (when user closes action UI)
286
+ figma.codegen.on('preferenceschange', (event) => {
287
+ // Preferences have been updated, refresh will happen automatically
288
+ // unless using action preferences that require manual refresh
289
+ });
290
+ ```
291
+
292
+ #### Manual Refresh
293
+
294
+ ```ts
295
+ // Force the Inspect panel to re-run the generate callback
296
+ figma.codegen.refresh();
297
+ ```
298
+
299
+ ---
300
+
301
+ ### Codegen Preference Types
302
+
303
+ #### Unit Preferences
304
+
305
+ Allow users to choose a CSS unit and scale factor (e.g., px to rem conversion).
306
+
307
+ ```json
308
+ {
309
+ "itemType": "unit",
310
+ "propertyName": "Size Unit",
311
+ "scaledUnit": "rem",
312
+ "defaultScaleFactor": 16,
313
+ "includedLanguages": ["react", "html-css"]
314
+ }
315
+ ```
316
+
317
+ - Defined once per language set
318
+ - Accessible via `figma.codegen.preferences.unit` and `.scaleFactor`
319
+ - Changing the preference automatically triggers the generate callback
320
+
321
+ #### Select Preferences
322
+
323
+ Multiple-choice dropdowns for formatting options.
324
+
325
+ ```json
326
+ {
327
+ "itemType": "select",
328
+ "propertyName": "Style Format",
329
+ "options": [
330
+ { "label": "CSS Modules", "value": "modules", "isDefault": true },
331
+ { "label": "Tailwind CSS", "value": "tailwind" },
332
+ { "label": "Styled Components", "value": "styled" }
333
+ ],
334
+ "includedLanguages": ["react"]
335
+ }
336
+ ```
337
+
338
+ - Accessible via `figma.codegen.preferences['Style Format']`
339
+ - Changing the selection automatically triggers the generate callback
340
+
341
+ #### Action Preferences
342
+
343
+ Open a custom UI for complex configuration (e.g., component mapping tables).
344
+
345
+ ```json
346
+ {
347
+ "itemType": "action",
348
+ "propertyName": "Component Config",
349
+ "label": "Configure Components"
350
+ }
351
+ ```
352
+
353
+ - Clicking the action button opens the UI via `figma.showUI()`
354
+ - Data should be stored via `figma.clientStorage`
355
+ - Requires manual `figma.codegen.refresh()` after changes
356
+ - The UI must call `figma.closePlugin()` or be hidden when done
357
+
358
+ ```ts
359
+ // Handle action preference
360
+ figma.codegen.on('preferenceschange', async (event) => {
361
+ if (event.propertyName === 'Component Config') {
362
+ // Show UI for configuration
363
+ figma.showUI(__html__, { width: 500, height: 400 });
364
+ }
365
+ });
366
+
367
+ // When UI sends updated config
368
+ figma.ui.on('message', async (msg) => {
369
+ if (msg.type === 'CONFIG_SAVED') {
370
+ await figma.clientStorage.setAsync('componentConfig', msg.config);
371
+ figma.ui.hide();
372
+ figma.codegen.refresh(); // Re-run generate with new config
373
+ }
374
+ });
375
+ ```
376
+
377
+ ---
378
+
379
+ ### Codegen Plugin Patterns
380
+
381
+ #### Basic React Code Generation
382
+
383
+ ```ts
384
+ figma.codegen.on('generate', (event) => {
385
+ const { node, language } = event;
386
+
387
+ if (language !== 'react') return [];
388
+
389
+ const componentName = toPascalCase(node.name);
390
+ const styles = generateStyles(node);
391
+ const jsx = generateJSX(node);
392
+
393
+ return [
394
+ {
395
+ title: `${componentName}.tsx`,
396
+ language: 'TYPESCRIPT',
397
+ code: [
398
+ `import styles from './${componentName}.module.css';`,
399
+ '',
400
+ `export function ${componentName}() {`,
401
+ ` return (`,
402
+ ` ${jsx}`,
403
+ ` );`,
404
+ `}`,
405
+ ].join('\n')
406
+ },
407
+ {
408
+ title: `${componentName}.module.css`,
409
+ language: 'CSS',
410
+ code: styles
411
+ }
412
+ ];
413
+ });
414
+
415
+ function toPascalCase(str: string): string {
416
+ return str
417
+ .replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
418
+ .replace(/^./, (c) => c.toUpperCase());
419
+ }
420
+ ```
421
+
422
+ #### Multi-Language Support
423
+
424
+ ```ts
425
+ figma.codegen.on('generate', (event) => {
426
+ const { node, language } = event;
427
+
428
+ switch (language) {
429
+ case 'react':
430
+ return generateReact(node);
431
+ case 'vue':
432
+ return generateVue(node);
433
+ case 'html-css':
434
+ return generateHTML(node);
435
+ case 'tailwind':
436
+ return generateTailwind(node);
437
+ default:
438
+ return [{
439
+ title: 'Unsupported',
440
+ code: `// Language "${language}" not yet supported`,
441
+ language: 'PLAINTEXT'
442
+ }];
443
+ }
444
+ });
445
+ ```
446
+
447
+ #### Reading Layout Properties for CSS
448
+
449
+ ```ts
450
+ function generateLayoutCSS(node: SceneNode): string {
451
+ const rules: string[] = [];
452
+
453
+ if ('layoutMode' in node && node.layoutMode !== 'NONE') {
454
+ rules.push('display: flex;');
455
+ rules.push(`flex-direction: ${node.layoutMode === 'HORIZONTAL' ? 'row' : 'column'};`);
456
+
457
+ if (node.itemSpacing > 0) {
458
+ rules.push(`gap: ${node.itemSpacing}px;`);
459
+ }
460
+
461
+ if (node.layoutWrap === 'WRAP') {
462
+ rules.push('flex-wrap: wrap;');
463
+ }
464
+
465
+ // Primary axis alignment (justify-content)
466
+ const justifyMap: Record<string, string> = {
467
+ 'MIN': 'flex-start',
468
+ 'CENTER': 'center',
469
+ 'MAX': 'flex-end',
470
+ 'SPACE_BETWEEN': 'space-between',
471
+ };
472
+ rules.push(`justify-content: ${justifyMap[node.primaryAxisAlignItems] || 'flex-start'};`);
473
+
474
+ // Cross axis alignment (align-items)
475
+ const alignMap: Record<string, string> = {
476
+ 'MIN': 'flex-start',
477
+ 'CENTER': 'center',
478
+ 'MAX': 'flex-end',
479
+ 'BASELINE': 'baseline',
480
+ };
481
+ rules.push(`align-items: ${alignMap[node.counterAxisAlignItems] || 'flex-start'};`);
482
+
483
+ // Padding
484
+ const { paddingTop, paddingRight, paddingBottom, paddingLeft } = node;
485
+ if (paddingTop || paddingRight || paddingBottom || paddingLeft) {
486
+ rules.push(`padding: ${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px;`);
487
+ }
488
+ }
489
+
490
+ // Sizing
491
+ if ('layoutSizingHorizontal' in node) {
492
+ if (node.layoutSizingHorizontal === 'FIXED') {
493
+ rules.push(`width: ${node.width}px;`);
494
+ } else if (node.layoutSizingHorizontal === 'FILL') {
495
+ rules.push('flex: 1 0 0;');
496
+ rules.push('width: 100%;');
497
+ }
498
+ // HUG: width is auto (no explicit CSS needed)
499
+ }
500
+
501
+ if ('layoutSizingVertical' in node) {
502
+ if (node.layoutSizingVertical === 'FIXED') {
503
+ rules.push(`height: ${node.height}px;`);
504
+ } else if (node.layoutSizingVertical === 'FILL') {
505
+ rules.push('flex: 1 0 0;');
506
+ rules.push('height: 100%;');
507
+ }
508
+ // HUG: height is auto (no explicit CSS needed)
509
+ }
510
+
511
+ return rules.join('\n');
512
+ }
513
+ ```
514
+
515
+ #### Handling Component Instances vs Definitions
516
+
517
+ ```ts
518
+ figma.codegen.on('generate', async ({ node, language }) => {
519
+ if (node.type === 'INSTANCE') {
520
+ const mainComponent = await node.getMainComponentAsync();
521
+ if (mainComponent) {
522
+ const componentName = toPascalCase(mainComponent.name);
523
+ const props = extractOverrides(node, mainComponent);
524
+
525
+ return [{
526
+ title: 'Usage',
527
+ language: 'TYPESCRIPT',
528
+ code: `<${componentName} ${formatProps(props)} />`
529
+ }];
530
+ }
531
+ }
532
+
533
+ if (node.type === 'COMPONENT') {
534
+ // Generate the component definition
535
+ return generateComponentDefinition(node, language);
536
+ }
537
+
538
+ // For other nodes, generate inline code
539
+ return generateInlineElement(node, language);
540
+ });
541
+ ```
542
+
543
+ #### Using iframe for Complex Processing in Codegen
544
+
545
+ Although codegen plugins typically do not have a visible UI, they can use a hidden iframe for operations that need browser APIs:
546
+
547
+ ```ts
548
+ let nextMessageIndex = 1;
549
+ const resolvers: Record<number, (result: CodegenResult[]) => void> = {};
550
+
551
+ // Show hidden UI for processing
552
+ figma.showUI('<script>/* processing code */</script>', { visible: false });
553
+
554
+ figma.ui.on('message', (msg) => {
555
+ if (msg.type === 'CODEGEN_RESULT' && resolvers[msg.messageIdx]) {
556
+ resolvers[msg.messageIdx](msg.results);
557
+ delete resolvers[msg.messageIdx];
558
+ }
559
+ });
560
+
561
+ figma.codegen.on('generate', async ({ node, language }) => {
562
+ const messageIdx = nextMessageIndex++;
563
+
564
+ return new Promise<CodegenResult[]>((resolve) => {
565
+ resolvers[messageIdx] = resolve;
566
+ figma.ui.postMessage({
567
+ type: 'GENERATE',
568
+ messageIdx,
569
+ nodeData: serializeNode(node),
570
+ language
571
+ });
572
+ });
573
+ });
574
+ ```
575
+
576
+ ---
577
+
578
+ ### Dev Resources REST API
579
+
580
+ Dev Resources are links attached to Figma nodes that point to external developer references (code repos, documentation, Storybook, etc.). They are visible in the Dev Mode Inspect panel.
581
+
582
+ #### Authentication
583
+
584
+ All Dev Resources endpoints require a Figma API token (PAT or OAuth) with the appropriate scope:
585
+
586
+ | Operation | Scope Required |
587
+ |-----------|---------------|
588
+ | Read | `file_dev_resources:read` |
589
+ | Create / Update / Delete | `file_dev_resources:write` |
590
+
591
+ All Dev Resources endpoints are **Rate limit tier: Tier 2**.
592
+
593
+ #### GET /v1/files/:file_key/dev_resources
594
+
595
+ Retrieve dev resources from a file.
596
+
597
+ ```bash
598
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
599
+ "https://api.figma.com/v1/files/FILE_KEY/dev_resources"
600
+ ```
601
+
602
+ **Optional query parameters:**
603
+
604
+ | Parameter | Type | Description |
605
+ |-----------|------|-------------|
606
+ | `node_ids` | string | Comma-separated node IDs to filter results |
607
+
608
+ **Response:**
609
+
610
+ ```ts
611
+ interface GetDevResourcesResponse {
612
+ dev_resources: DevResource[];
613
+ }
614
+
615
+ interface DevResource {
616
+ id: string;
617
+ name: string;
618
+ url: string;
619
+ file_key: string;
620
+ node_id: string;
621
+ }
622
+ ```
623
+
624
+ #### POST /v1/dev_resources
625
+
626
+ Create dev resources in bulk (can span multiple files).
627
+
628
+ > **Critical:** The request body uses `dev_resources` (plural array), NOT a singular `dev_resource` object. This was specifically flagged in the audit as a common error.
629
+
630
+ ```bash
631
+ curl -X POST \
632
+ -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
633
+ -H "Content-Type: application/json" \
634
+ -d '{
635
+ "dev_resources": [
636
+ {
637
+ "name": "React Component",
638
+ "url": "https://github.com/org/repo/blob/main/src/Button.tsx",
639
+ "file_key": "FILE_KEY",
640
+ "node_id": "1:2"
641
+ },
642
+ {
643
+ "name": "Storybook",
644
+ "url": "https://storybook.example.com/?path=/story/button",
645
+ "file_key": "FILE_KEY",
646
+ "node_id": "1:2"
647
+ }
648
+ ]
649
+ }' \
650
+ "https://api.figma.com/v1/dev_resources"
651
+ ```
652
+
653
+ **Response:**
654
+
655
+ ```ts
656
+ interface CreateDevResourcesResponse {
657
+ links_created: DevResource[];
658
+ errors: Array<{
659
+ file_key: string;
660
+ node_id: string;
661
+ error: string;
662
+ }>;
663
+ }
664
+ ```
665
+
666
+ **Constraints:**
667
+
668
+ | Constraint | Limit |
669
+ |-----------|-------|
670
+ | Max dev resources per node | 10 |
671
+ | Duplicate URLs per node | Not allowed |
672
+ | File key | Must be valid and accessible |
673
+
674
+ #### PUT /v1/dev_resources
675
+
676
+ Update dev resources in bulk.
677
+
678
+ ```bash
679
+ curl -X PUT \
680
+ -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
681
+ -H "Content-Type: application/json" \
682
+ -d '{
683
+ "dev_resources": [
684
+ {
685
+ "id": "dev-resource-id-123",
686
+ "name": "Updated Component Name",
687
+ "url": "https://github.com/org/repo/blob/main/src/NewButton.tsx"
688
+ }
689
+ ]
690
+ }' \
691
+ "https://api.figma.com/v1/dev_resources"
692
+ ```
693
+
694
+ **Response:**
695
+
696
+ ```ts
697
+ interface UpdateDevResourcesResponse {
698
+ links_updated: string[]; // IDs of successfully updated resources
699
+ errors: Array<{
700
+ id: string;
701
+ error: string;
702
+ }>;
703
+ }
704
+ ```
705
+
706
+ #### DELETE /v1/files/:file_key/dev_resources/:dev_resource_id
707
+
708
+ Delete a single dev resource.
709
+
710
+ ```bash
711
+ curl -X DELETE \
712
+ -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
713
+ "https://api.figma.com/v1/files/FILE_KEY/dev_resources/DEV_RESOURCE_ID"
714
+ ```
715
+
716
+ Returns no content on success.
717
+
718
+ ---
719
+
720
+ ### Dev Resources Use Cases
721
+
722
+ #### Linking Components to Source Code
723
+
724
+ After generating code from Figma components, attach links back to the generated files:
725
+
726
+ ```ts
727
+ async function linkComponentsToCode(
728
+ fileKey: string,
729
+ mappings: Array<{ nodeId: string; repoUrl: string; componentName: string }>
730
+ ) {
731
+ const response = await fetch('https://api.figma.com/v1/dev_resources', {
732
+ method: 'POST',
733
+ headers: {
734
+ 'X-Figma-Token': process.env.FIGMA_TOKEN!,
735
+ 'Content-Type': 'application/json',
736
+ },
737
+ body: JSON.stringify({
738
+ dev_resources: mappings.map(m => ({
739
+ name: `${m.componentName} (Source)`,
740
+ url: m.repoUrl,
741
+ file_key: fileKey,
742
+ node_id: m.nodeId,
743
+ })),
744
+ }),
745
+ });
746
+
747
+ const result = await response.json();
748
+
749
+ if (result.errors?.length > 0) {
750
+ console.warn('Some dev resources failed:', result.errors);
751
+ }
752
+
753
+ return result.links_created;
754
+ }
755
+ ```
756
+
757
+ #### Surfacing Generated Code in Dev Mode
758
+
759
+ Create dev resources that link to generated code output (e.g., a code preview URL):
760
+
761
+ ```ts
762
+ async function attachCodePreview(
763
+ fileKey: string,
764
+ nodeId: string,
765
+ previewUrl: string
766
+ ) {
767
+ await fetch('https://api.figma.com/v1/dev_resources', {
768
+ method: 'POST',
769
+ headers: {
770
+ 'X-Figma-Token': process.env.FIGMA_TOKEN!,
771
+ 'Content-Type': 'application/json',
772
+ },
773
+ body: JSON.stringify({
774
+ dev_resources: [{
775
+ name: 'Generated Code Preview',
776
+ url: previewUrl,
777
+ file_key: fileKey,
778
+ node_id: nodeId,
779
+ }],
780
+ }),
781
+ });
782
+ }
783
+ ```
784
+
785
+ #### Design-to-Code Traceability
786
+
787
+ Build a bidirectional link between Figma designs and implemented code:
788
+
789
+ ```ts
790
+ // 1. Fetch all dev resources for a file
791
+ async function getDevResources(fileKey: string): Promise<DevResource[]> {
792
+ const res = await fetch(
793
+ `https://api.figma.com/v1/files/${fileKey}/dev_resources`,
794
+ { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
795
+ );
796
+ const data = await res.json();
797
+ return data.dev_resources;
798
+ }
799
+
800
+ // 2. Build a node → code URL mapping
801
+ async function buildTraceabilityMap(fileKey: string): Promise<Map<string, string[]>> {
802
+ const resources = await getDevResources(fileKey);
803
+ const map = new Map<string, string[]>();
804
+
805
+ for (const resource of resources) {
806
+ const urls = map.get(resource.node_id) || [];
807
+ urls.push(resource.url);
808
+ map.set(resource.node_id, urls);
809
+ }
810
+
811
+ return map;
812
+ }
813
+ ```
814
+
815
+ ---
816
+
817
+ ### Dev Mode Annotations
818
+
819
+ Annotations in Dev Mode provide additional context for developers inspecting designs.
820
+
821
+ #### Ready for Dev Status
822
+
823
+ Designers mark frames or sections as "Ready for dev" to signal that a design is complete and ready for implementation. This status is visible in Dev Mode and can be used to filter which components to process.
824
+
825
+ #### Plugin API Access
826
+
827
+ ```ts
828
+ // Access annotations on a node
829
+ if ('annotations' in node) {
830
+ const annotations = node.annotations;
831
+ // Annotations contain measurements, notes, and status information
832
+ }
833
+ ```
834
+
835
+ #### How Annotations Relate to Dev Resources
836
+
837
+ - **Annotations** are designer-created notes about a design's intent, measurements, and specifications. They live within Figma.
838
+ - **Dev Resources** are links to external developer references (code, docs, stories). They are created via the REST API or manually in Dev Mode.
839
+
840
+ Both appear in the Inspect panel and together provide a complete handoff context: annotations explain the "what" and "why" of the design, while dev resources point to the "where" and "how" of the implementation.
841
+
842
+ ---
843
+
844
+ ### Codegen vs Standard Plugin: Quick Comparison
845
+
846
+ | Aspect | Standard Plugin | Codegen Plugin |
847
+ |--------|:--------------:|:--------------:|
848
+ | `editorType` | `["figma"]` or `["figma", "figjam"]` | `["dev"]` |
849
+ | `capabilities` | None required | `["codegen"]` (required) |
850
+ | UI | iframe via `figma.showUI()` | No visible UI (code in Inspect panel) |
851
+ | Trigger | User runs from Plugins menu | User selects node in Dev Mode |
852
+ | Output | Side effects (node creation, export, etc.) | `CodegenResult[]` displayed in Inspect panel |
853
+ | Document access | Full read/write | Read-only |
854
+ | Primary use case | Design automation, export, analysis | Code generation for developers |
855
+ | `figma.mode` | `"default"` | `"codegen"` |
856
+ | Network access | Via UI iframe proxy | Via UI iframe proxy (if using hidden UI) |
857
+ | Timeout | None (runs until `closePlugin()`) | 3-second timeout on generate callback |
858
+
859
+ ---
860
+
861
+ ### Error Handling
862
+
863
+ #### Codegen Plugin Errors
864
+
865
+ | Error | Cause | Solution |
866
+ |-------|-------|----------|
867
+ | Generate callback timeout | Callback exceeds 3 seconds | Optimize code generation; pre-cache data; use simpler algorithms |
868
+ | Node properties unavailable | Accessing properties not on the node type | Check `node.type` before accessing type-specific properties |
869
+ | Font not loaded | Accessing text properties without loading font | Call `figma.loadFontAsync()` before reading text properties |
870
+
871
+ #### Dev Resources REST API Errors
872
+
873
+ | Status | Meaning | Action |
874
+ |--------|---------|--------|
875
+ | `400` | Invalid parameters (bad node_id, invalid URL) | Verify request body structure and field values |
876
+ | `401` | Authentication failed | Verify token and `file_dev_resources:read/write` scope |
877
+ | `403` | Insufficient permissions | Confirm file access permissions |
878
+ | `404` | Dev resource not found (DELETE only) | Verify the dev_resource_id exists |
879
+ | `429` | Rate limited | Use `Retry-After` header. See `figma-api-rest.md` for backoff strategy |
880
+
881
+ ---
882
+
883
+ ## Cross-References
884
+
885
+ - **`figma-api-rest.md`** — Core REST API reference (authentication, rate limits, file endpoints)
886
+ - **`figma-api-plugin.md`** — Standard plugin development (sandbox model, SceneNode types, IPC patterns)
887
+ - **`figma-api-variables.md`** — Variables API for design tokens (consumed during code generation)
888
+ - **`figma-api-webhooks.md`** — Webhooks v2 DEV_MODE_STATUS_UPDATE event for reacting to "Ready for dev" changes
889
+ - **`design-to-code-layout.md`** — Auto Layout to Flexbox mapping rules (used in codegen callbacks)
890
+ - **`design-to-code-visual.md`** — Visual property extraction (fills, strokes, effects for CSS generation)
891
+ - **`design-to-code-typography.md`** — Typography mapping (font families, sizes, line height for CSS)
892
+ - **`design-to-code-semantic.md`** — Semantic HTML generation patterns
893
+ - **`css-strategy.md`** — Layered CSS approach (Tailwind + CSS Custom Properties + CSS Modules)
894
+ - **`plugin-codegen.md`** — Production codegen plugin development patterns (generation pipeline, preferences, code quality, responsive output)