figma

Integrate with Figma API for design automation and code generation. Use when extracting design tokens, generating React/CSS code from Figma components, syncing…

INSTALLATION
npx skills add https://github.com/hoodini/ai-agents-skills --skill figma
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

// Get file

const response = await fetch(

https://api.figma.com/v1/files/${FILE_KEY},

{ headers }

);

### File Key & Node IDs

// Extract from Figma URL: figma.com/file/FILE_KEY/Name?node-id=NODE_ID

const figmaUrl = 'https://www.figma.com/file/abc123/MyDesign?node-id=1%3A2';

const fileKey = figmaUrl.match(/file\/([^/]+)/)?.[1]; // abc123

const nodeId = new URL(figmaUrl).searchParams.get('node-id'); // 1:2


## Core API Endpoints

### Get File

// Full file

GET https://api.figma.com/v1/files/:file_key

// Specific nodes (components)

GET https://api.figma.com/v1/files/:file_key/nodes?ids=1:2,1:3

// With geometry for SVG paths

GET https://api.figma.com/v1/files/:file_key?geometry=paths

// With plugin data

GET https://api.figma.com/v1/files/:file_key?plugin_data=shared


### Get Components

// Get all components in a file

GET https://api.figma.com/v1/files/:file_key/components

// Get component sets (variants)

GET https://api.figma.com/v1/files/:file_key/component_sets

// Get team's published components

GET https://api.figma.com/v1/teams/:team_id/components


### Export Images

// Export nodes as images

GET https://api.figma.com/v1/images/:file_key?ids=1:2,1:3&format=png&scale=2

// Export as SVG

GET https://api.figma.com/v1/images/:file_key?ids=1:2&format=svg

// Response

{

"images": {

"1:2": "https://s3.amazonaws.com/...",

"1:3": "https://s3.amazonaws.com/..."

}

}


## Component Code Generation

### Figma Node to React Component

interface FigmaNode {

id: string;

name: string;

type: string;

children?: FigmaNode[];

absoluteBoundingBox?: { x: number; y: number; width: number; height: number };

fills?: Fill[];

strokes?: Stroke[];

effects?: Effect[];

cornerRadius?: number;

paddingLeft?: number;

paddingRight?: number;

paddingTop?: number;

paddingBottom?: number;

itemSpacing?: number;

layoutMode?: 'HORIZONTAL' | 'VERTICAL' | 'NONE';

primaryAxisAlignItems?: string;

counterAxisAlignItems?: string;

characters?: string;

style?: TextStyle;

}

async function getComponentCode(fileKey: string, nodeId: string): Promise<string> {

const response = await fetch(

https://api.figma.com/v1/files/${fileKey}/nodes?ids=${nodeId},

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

const data = await response.json();

const node = data.nodes[nodeId].document;

return generateReactComponent(node);

}

function generateReactComponent(node: FigmaNode): string {

const componentName = toPascalCase(node.name);

const styles = extractStyles(node);

const children = node.children?.map(child => generateJSX(child)).join('\n') || '';

return

import React from 'react';

export function ${componentName}() {

return (

<div style={${JSON.stringify(styles, null, 2)}}>

${children}

</div>

);

}

;

}

function extractStyles(node: FigmaNode): React.CSSProperties {

const styles: React.CSSProperties = {};

// Dimensions

if (node.absoluteBoundingBox) {

styles.width = node.absoluteBoundingBox.width;

styles.height = node.absoluteBoundingBox.height;

}

// Background

if (node.fills?.length) {

const fill = node.fills.find(f => f.visible !== false &#x26;&#x26; f.type === 'SOLID');

if (fill?.color) {

styles.backgroundColor = rgbaToHex(fill.color);

}

}

// Border radius

if (node.cornerRadius) {

styles.borderRadius = node.cornerRadius;

}

// Padding

if (node.paddingLeft) styles.paddingLeft = node.paddingLeft;

if (node.paddingRight) styles.paddingRight = node.paddingRight;

if (node.paddingTop) styles.paddingTop = node.paddingTop;

if (node.paddingBottom) styles.paddingBottom = node.paddingBottom;

// Flexbox (Auto Layout)

if (node.layoutMode &#x26;&#x26; node.layoutMode !== 'NONE') {

styles.display = 'flex';

styles.flexDirection = node.layoutMode === 'HORIZONTAL' ? 'row' : 'column';

styles.gap = node.itemSpacing;

// Alignment

const alignMap: Record<string, string> = {

'MIN': 'flex-start',

'CENTER': 'center',

'MAX': 'flex-end',

'SPACE_BETWEEN': 'space-between',

};

if (node.primaryAxisAlignItems) {

styles.justifyContent = alignMap[node.primaryAxisAlignItems] || 'flex-start';

}

if (node.counterAxisAlignItems) {

styles.alignItems = alignMap[node.counterAxisAlignItems] || 'flex-start';

}

}

return styles;

}

function generateJSX(node: FigmaNode): string {

const styles = extractStyles(node);

if (node.type === 'TEXT') {

return <span style={${JSON.stringify(styles)}}>${node.characters || ''}</span>;

}

if (node.type === 'VECTOR' || node.type === 'BOOLEAN_OPERATION') {

return {/* Vector: ${node.name} - export as SVG */};

}

const children = node.children?.map(child => generateJSX(child)).join('\n') || '';

return <div style={${JSON.stringify(styles)}}>${children}</div>;

}


### Generate Tailwind CSS from Figma

function figmaToTailwind(node: FigmaNode): string {

const classes: string[] = [];

// Dimensions

if (node.absoluteBoundingBox) {

const { width, height } = node.absoluteBoundingBox;

classes.push(w-[${Math.round(width)}px], h-[${Math.round(height)}px]);

}

// Background color

if (node.fills?.length) {

const fill = node.fills.find(f => f.visible !== false &#x26;&#x26; f.type === 'SOLID');

if (fill?.color) {

const hex = rgbaToHex(fill.color);

classes.push(bg-[${hex}]);

}

}

// Border radius

if (node.cornerRadius) {

const radiusMap: Record<number, string> = {

4: 'rounded-sm', 6: 'rounded-md', 8: 'rounded-lg',

12: 'rounded-xl', 16: 'rounded-2xl', 9999: 'rounded-full'

};

classes.push(radiusMap[node.cornerRadius] || rounded-[${node.cornerRadius}px]);

}

// Padding

if (node.paddingLeft === node.paddingRight &#x26;&#x26;

node.paddingTop === node.paddingBottom &#x26;&#x26;

node.paddingLeft === node.paddingTop) {

classes.push(p-[${node.paddingLeft}px]);

} else {

if (node.paddingLeft) classes.push(pl-[${node.paddingLeft}px]);

if (node.paddingRight) classes.push(pr-[${node.paddingRight}px]);

if (node.paddingTop) classes.push(pt-[${node.paddingTop}px]);

if (node.paddingBottom) classes.push(pb-[${node.paddingBottom}px]);

}

// Flexbox

if (node.layoutMode &#x26;&#x26; node.layoutMode !== 'NONE') {

classes.push('flex');

classes.push(node.layoutMode === 'HORIZONTAL' ? 'flex-row' : 'flex-col');

if (node.itemSpacing) classes.push(gap-[${node.itemSpacing}px]);

const justifyMap: Record<string, string> = {

'MIN': 'justify-start', 'CENTER': 'justify-center',

'MAX': 'justify-end', 'SPACE_BETWEEN': 'justify-between'

};

const alignMap: Record<string, string> = {

'MIN': 'items-start', 'CENTER': 'items-center', 'MAX': 'items-end'

};

if (node.primaryAxisAlignItems) classes.push(justifyMap[node.primaryAxisAlignItems]);

if (node.counterAxisAlignItems) classes.push(alignMap[node.counterAxisAlignItems]);

}

return classes.join(' ');

}


### Extract All Components from File

interface ComponentInfo {

key: string;

name: string;

description: string;

nodeId: string;

thumbnailUrl?: string;

}

async function getAllComponents(fileKey: string): Promise<ComponentInfo[]> {

const response = await fetch(

https://api.figma.com/v1/files/${fileKey}/components,

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

const data = await response.json();

return data.meta.components.map((comp: any) => ({

key: comp.key,

name: comp.name,

description: comp.description || '',

nodeId: comp.node_id,

thumbnailUrl: comp.thumbnail_url,

}));

}

// Generate code for all components

async function generateAllComponentsCode(fileKey: string): Promise<Map<string, string>> {

const components = await getAllComponents(fileKey);

const codeMap = new Map<string, string>();

for (const comp of components) {

const code = await getComponentCode(fileKey, comp.nodeId);

codeMap.set(comp.name, code);

}

return codeMap;

}


## Design Token Extraction

### Extract Colors

async function extractColors(fileKey: string) {

const file = await fetch(

https://api.figma.com/v1/files/${fileKey}/styles,

{ headers }

).then(r => r.json());

const colors = {};

for (const style of file.meta.styles) {

if (style.style_type === 'FILL') {

const nodeData = await getStyleNode(fileKey, style.node_id);

const fill = nodeData.fills[0];

if (fill.type === 'SOLID') {

colors[style.name] = rgbaToHex(fill.color);

}

}

}

return colors;

}

function rgbaToHex({ r, g, b, a = 1 }) {

const toHex = (n) => Math.round(n * 255).toString(16).padStart(2, '0');

return #${toHex(r)}${toHex(g)}${toHex(b)}${a < 1 ? toHex(a) : ''};

}


### Extract Typography Tokens

async function extractTypography(fileKey: string) {

const response = await fetch(

https://api.figma.com/v1/files/${fileKey}/styles,

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

const data = await response.json();

const typography: Record<string, any> = {};

for (const style of data.meta.styles) {

if (style.style_type === 'TEXT') {

const nodeResponse = await fetch(

https://api.figma.com/v1/files/${fileKey}/nodes?ids=${style.node_id},

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

const nodeData = await nodeResponse.json();

const node = nodeData.nodes[style.node_id].document;

typography[style.name] = {

fontFamily: node.style?.fontFamily,

fontWeight: node.style?.fontWeight,

fontSize: node.style?.fontSize,

lineHeight: node.style?.lineHeightPx,

letterSpacing: node.style?.letterSpacing,

};

}

}

return typography;

}


### Generate Complete Design Tokens

interface DesignTokens {

colors: Record<string, string>;

typography: Record<string, any>;

spacing: Record<string, number>;

shadows: Record<string, string>;

radii: Record<string, number>;

}

async function generateDesignTokens(fileKey: string): Promise<DesignTokens> {

const [colors, typography] = await Promise.all([

extractColors(fileKey),

extractTypography(fileKey),

]);

return {

colors,

typography,

spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }, // Extract from file

shadows: {}, // Extract from effect styles

radii: {}, // Extract from nodes

};

}

// Generate CSS Variables

function tokensToCSSVariables(tokens: DesignTokens): string {

let css = ':root {\n';

// Colors

for (const [name, value] of Object.entries(tokens.colors)) {

const cssName = name.toLowerCase().replace(/[\s/]+/g, '-');

css += --color-${cssName}: ${value};\n;

}

// Typography

for (const [name, style] of Object.entries(tokens.typography)) {

const cssName = name.toLowerCase().replace(/[\s/]+/g, '-');

css += --font-${cssName}-family: "${style.fontFamily}";\n;

css += --font-${cssName}-size: ${style.fontSize}px;\n;

css += --font-${cssName}-weight: ${style.fontWeight};\n;

css += --font-${cssName}-line-height: ${style.lineHeight}px;\n;

}

css += '}\n';

return css;

}

// Generate Tailwind Config

function tokensToTailwindConfig(tokens: DesignTokens): string {

const colors: Record<string, string> = {};

for (const [name, value] of Object.entries(tokens.colors)) {

const key = name.toLowerCase().replace(/[\s/]+/g, '-');

colors[key] = value;

}

return

module.exports = {

theme: {

extend: {

colors: ${JSON.stringify(colors, null, 6)},

fontFamily: {

${Object.entries(tokens.typography).map(([name, style]) =>

'${name.toLowerCase()}': ['${style.fontFamily}', 'sans-serif']

).join(',\n ')}

},

},

},

};

;

}


## Variables API (Design Tokens 2.0)

// Get variables (requires enterprise/organization plan)

GET https://api.figma.com/v1/files/:file_key/variables/local

// Get variable collections

GET https://api.figma.com/v1/files/:file_key/variables/local/collections

// Response structure

interface VariableCollection {

id: string;

name: string;

modes: { modeId: string; name: string }[];

variableIds: string[];

}

interface Variable {

id: string;

name: string;

resolvedType: 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR';

valuesByMode: Record<string, any>;

}

async function getVariables(fileKey: string) {

const response = await fetch(

https://api.figma.com/v1/files/${fileKey}/variables/local,

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

return response.json();

}


## Dev Mode Integration

### Code Snippets from Dev Mode

// Get Dev Resources (annotations, measurements)

GET https://api.figma.com/v1/files/:file_key/dev_resources

// Create Dev Resource

POST https://api.figma.com/v1/dev_resources

{

"dev_resource": {

"name": "React Component",

"url": "https://github.com/...",

"file_key": "abc123",

"node_id": "1:2"

}

}


## Webhooks

### Setup Webhook

POST https://api.figma.com/v2/webhooks

{

"event_type": "FILE_UPDATE",

"team_id": "123456",

"endpoint": "https://your-server.com/figma-webhook",

"passcode": "your-secret-passcode"

}

// Available events:

// - FILE_UPDATE

// - FILE_DELETE

// - FILE_VERSION_UPDATE

// - LIBRARY_PUBLISH

// - FILE_COMMENT


### Handle Webhook

app.post('/figma-webhook', async (req, res) => {

const { passcode } = req.body;

if (passcode !== process.env.FIGMA_WEBHOOK_SECRET) {

return res.status(401).send('Unauthorized');

}

const { event_type, file_key, timestamp } = req.body;

switch (event_type) {

case 'FILE_UPDATE':

await syncDesignTokens(file_key);

break;

case 'LIBRARY_PUBLISH':

await regenerateComponents(file_key);

break;

case 'FILE_COMMENT':

await notifyTeam(req.body);

break;

}

res.status(200).send('OK');

});


## Complete Design-to-Code Pipeline

// Full pipeline: Figma file → React components + tokens

async function figmaToCode(fileKey: string, outputDir: string) {

// 1. Get all components

const components = await getAllComponents(fileKey);

// 2. Generate design tokens

const tokens = await generateDesignTokens(fileKey);

await fs.writeFile(

${outputDir}/tokens.css,

tokensToCSSVariables(tokens)

);

await fs.writeFile(

${outputDir}/tailwind.config.js,

tokensToTailwindConfig(tokens)

);

// 3. Generate React components

for (const comp of components) {

const code = await getComponentCode(fileKey, comp.nodeId);

const fileName = toPascalCase(comp.name) + '.tsx';

await fs.writeFile(${outputDir}/components/${fileName}, code);

}

// 4. Export icons as SVGs

const icons = components.filter(c => c.name.startsWith('Icon/'));

if (icons.length) {

const iconIds = icons.map(i => i.nodeId).join(',');

const svgResponse = await fetch(

https://api.figma.com/v1/images/${fileKey}?ids=${iconIds}&#x26;format=svg,

{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }

);

const svgData = await svgResponse.json();

for (const icon of icons) {

const svgUrl = svgData.images[icon.nodeId];

const svg = await fetch(svgUrl).then(r => r.text());

await fs.writeFile(${outputDir}/icons/${icon.name}.svg, svg);

}

}

console.log(Generated ${components.length} components and ${Object.keys(tokens.colors).length} color tokens);

}


## Figma Plugin Development

### Plugin Manifest (manifest.json)

{

"name": "My Figma Plugin",

"id": "123456789",

"api": "1.0.0",

"main": "code.js",

"ui": "ui.html",

"editorType": ["figma", "figjam"],

"capabilities": ["codegen"],

"codegenLanguages": [

{ "label": "React", "value": "react" },

{ "label": "Vue", "value": "vue" }

]

}


### Plugin Code (code.ts)

// Show UI

figma.showUI(__html__, { width: 400, height: 500 });

// Handle selection

figma.on('selectionchange', () => {

const selection = figma.currentPage.selection;

figma.ui.postMessage({ type: 'selection', nodes: selection.map(nodeToJSON) });

});

// Handle messages from UI

figma.ui.onmessage = async (msg) => {

if (msg.type === 'export-code') {

const node = figma.currentPage.selection[0];

const code = generateCode(node);

figma.ui.postMessage({ type: 'code-generated', code });

}

};

function nodeToJSON(node: SceneNode) {

return {

id: node.id,

name: node.name,

type: node.type,

width: node.width,

height: node.height,

};

}

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card