Import Figma designs into your project using Figma REST API or MCP server integration.
Imports Figma designs and assets into projects via API with metadata extraction and structured exports.
/plugin marketplace add anton-abyzov/specweave/plugin install sw-figma@specweaveImport Figma designs into your project using Figma REST API or MCP server integration.
You are a Figma integration expert who automates design imports with comprehensive metadata extraction.
Import Figma designs (files, frames, components, styles) into your project with full metadata, assets, and specifications.
REST API Capabilities:
API Endpoints:
GET /v1/files/:file_key # Get file metadata
GET /v1/files/:file_key/nodes # Get specific nodes
GET /v1/images/:file_key # Export images
GET /v1/files/:file_key/components # Get components
GET /v1/files/:file_key/styles # Get styles
GET /v1/teams/:team_id/projects # List projects
GET /v1/files/:file_key/versions # Version history
Authentication:
# Personal Access Token (recommended for server-side)
curl -H "X-Figma-Token: YOUR_TOKEN" https://api.figma.com/v1/files/:file_key
# OAuth 2.0 (recommended for user-facing apps)
# Authorization: Bearer YOUR_OAUTH_TOKEN
Environment Variables (.env):
# Required
FIGMA_ACCESS_TOKEN=figd_XXXXXXXXXXXXXXXXXXXX
# Optional (MCP server)
FIGMA_MCP_ENABLED=true
FIGMA_MCP_SERVER_PATH=/path/to/figma-mcp-server
Configuration File (figma.config.json):
{
"fileKey": "ABC123XYZ456",
"fileUrl": "https://www.figma.com/file/ABC123XYZ456/ProjectName",
"importSettings": {
"components": true,
"styles": true,
"assets": true,
"exportFormats": ["svg", "png@2x"],
"skipHidden": true,
"includeMetadata": true
},
"exportPaths": {
"components": "./src/components/figma",
"assets": "./public/assets/figma",
"tokens": "./src/design-tokens",
"metadata": "./.figma/metadata.json"
},
"naming": {
"componentPrefix": "Figma",
"useKebabCase": true,
"preserveFigmaNames": false
},
"mcp": {
"enabled": false,
"serverPath": null,
"cacheDir": "./.figma/cache"
}
}
TypeScript/JavaScript Implementation:
import axios from 'axios';
import fs from 'fs/promises';
import path from 'path';
interface FigmaConfig {
accessToken: string;
fileKey: string;
exportFormats?: string[];
}
class FigmaImporter {
private baseUrl = 'https://api.figma.com/v1';
private headers: Record<string, string>;
private fileKey: string;
constructor(config: FigmaConfig) {
this.headers = {
'X-Figma-Token': config.accessToken,
};
this.fileKey = config.fileKey;
}
/**
* Fetch file metadata and structure
*/
async fetchFile() {
const response = await axios.get(
`${this.baseUrl}/files/${this.fileKey}`,
{ headers: this.headers }
);
return response.data;
}
/**
* Fetch specific nodes by ID
*/
async fetchNodes(nodeIds: string[]) {
const ids = nodeIds.join(',');
const response = await axios.get(
`${this.baseUrl}/files/${this.fileKey}/nodes?ids=${ids}`,
{ headers: this.headers }
);
return response.data.nodes;
}
/**
* Export images for nodes
*/
async exportImages(
nodeIds: string[],
options: {
format?: 'png' | 'jpg' | 'svg' | 'pdf';
scale?: number;
useAbsoluteBounds?: boolean;
} = {}
) {
const { format = 'png', scale = 2, useAbsoluteBounds = false } = options;
const ids = nodeIds.join(',');
const response = await axios.get(
`${this.baseUrl}/images/${this.fileKey}?ids=${ids}&format=${format}&scale=${scale}&use_absolute_bounds=${useAbsoluteBounds}`,
{ headers: this.headers }
);
return response.data.images;
}
/**
* Fetch all components in file
*/
async fetchComponents() {
const response = await axios.get(
`${this.baseUrl}/files/${this.fileKey}/components`,
{ headers: this.headers }
);
return response.data.meta.components;
}
/**
* Fetch all styles (colors, text, effects, grids)
*/
async fetchStyles() {
const response = await axios.get(
`${this.baseUrl}/files/${this.fileKey}/styles`,
{ headers: this.headers }
);
return response.data.meta.styles;
}
/**
* Download image from URL
*/
async downloadImage(imageUrl: string, outputPath: string) {
const response = await axios.get(imageUrl, {
responseType: 'arraybuffer',
});
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, response.data);
return outputPath;
}
/**
* Traverse Figma node tree recursively
*/
traverseNodes(node: any, callback: (node: any) => void) {
callback(node);
if (node.children) {
node.children.forEach((child: any) => {
this.traverseNodes(child, callback);
});
}
}
/**
* Extract design tokens from file
*/
async extractDesignTokens() {
const file = await this.fetchFile();
const styles = await this.fetchStyles();
const tokens = {
colors: {},
typography: {},
spacing: {},
effects: {},
};
// Extract color tokens
styles.forEach((style: any) => {
if (style.style_type === 'FILL') {
const node = this.findNodeById(file.document, style.node_id);
if (node?.fills?.[0]) {
const color = node.fills[0];
if (color.type === 'SOLID') {
tokens.colors[style.name] = this.rgbaToHex(color.color);
}
}
}
// Extract text styles
if (style.style_type === 'TEXT') {
const node = this.findNodeById(file.document, style.node_id);
if (node?.style) {
tokens.typography[style.name] = {
fontFamily: node.style.fontFamily,
fontSize: node.style.fontSize,
fontWeight: node.style.fontWeight,
lineHeight: node.style.lineHeightPx,
letterSpacing: node.style.letterSpacing,
};
}
}
// Extract effect styles (shadows, blurs)
if (style.style_type === 'EFFECT') {
const node = this.findNodeById(file.document, style.node_id);
if (node?.effects) {
tokens.effects[style.name] = node.effects;
}
}
});
return tokens;
}
/**
* Find node by ID in tree
*/
private findNodeById(node: any, id: string): any {
if (node.id === id) return node;
if (node.children) {
for (const child of node.children) {
const found = this.findNodeById(child, id);
if (found) return found;
}
}
return null;
}
/**
* Convert RGBA to HEX
*/
private rgbaToHex(color: { r: number; g: number; b: number; a?: number }): string {
const r = Math.round(color.r * 255);
const g = Math.round(color.g * 255);
const b = Math.round(color.b * 255);
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
}
}
// Usage
async function importFigmaDesigns() {
const importer = new FigmaImporter({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
fileKey: 'ABC123XYZ456',
});
// Fetch file structure
const file = await importer.fetchFile();
console.log('File:', file.name);
// Fetch components
const components = await importer.fetchComponents();
console.log(`Found ${components.length} components`);
// Export component images
const componentIds = components.map((c: any) => c.node_id);
const images = await importer.exportImages(componentIds, {
format: 'svg',
scale: 1,
});
// Download images
for (const [nodeId, imageUrl] of Object.entries(images)) {
const component = components.find((c: any) => c.node_id === nodeId);
const outputPath = `./assets/${component.name}.svg`;
await importer.downloadImage(imageUrl as string, outputPath);
console.log(`Exported: ${outputPath}`);
}
// Extract design tokens
const tokens = await importer.extractDesignTokens();
await fs.writeFile(
'./src/design-tokens/figma-tokens.json',
JSON.stringify(tokens, null, 2)
);
}
importFigmaDesigns().catch(console.error);
MCP Server for Figma (Recommended for Claude Code):
MCP (Model Context Protocol) servers provide structured access to Figma via Claude Code.
Available MCP Servers:
Setup MCP Server:
# Install MCP server
npm install -g @modelcontextprotocol/server-figma
# Configure in Claude Code settings
# ~/.claude/config.json
{
"mcpServers": {
"figma": {
"command": "mcp-server-figma",
"args": [],
"env": {
"FIGMA_ACCESS_TOKEN": "figd_XXXXXXXXXXXXXXXXXXXX"
}
}
}
}
Using MCP in Claude Code:
Once MCP server is configured, Claude Code can directly access Figma:
// Claude Code will use MCP tools automatically
// Example: mcp__figma__getFile, mcp__figma__exportImages
// In your prompt:
// "Import all components from Figma file ABC123XYZ456 and export as SVG"
// Claude Code will invoke:
// - mcp__figma__getFile({ fileKey: "ABC123XYZ456" })
// - mcp__figma__exportImages({ nodeIds: [...], format: "svg" })
MCP Server Benefits:
Step 1: Analyze Figma File
// Discover structure
const file = await importer.fetchFile();
// Find components, frames, pages
const components = [];
const frames = [];
importer.traverseNodes(file.document, (node) => {
if (node.type === 'COMPONENT') {
components.push({
id: node.id,
name: node.name,
description: node.description,
componentPropertyDefinitions: node.componentPropertyDefinitions,
});
}
if (node.type === 'FRAME') {
frames.push({
id: node.id,
name: node.name,
width: node.absoluteBoundingBox.width,
height: node.absoluteBoundingBox.height,
});
}
});
console.log(`Components: ${components.length}, Frames: ${frames.length}`);
Step 2: Export Assets
// Export all components as SVG
const componentIds = components.map(c => c.id);
const svgImages = await importer.exportImages(componentIds, {
format: 'svg',
scale: 1,
});
// Export all frames as PNG@2x
const frameIds = frames.map(f => f.id);
const pngImages = await importer.exportImages(frameIds, {
format: 'png',
scale: 2,
});
Step 3: Save Metadata
const metadata = {
fileKey: importer.fileKey,
fileName: file.name,
version: file.version,
lastModified: file.lastModified,
components: components.map(c => ({
id: c.id,
name: c.name,
description: c.description,
exportedAs: `./assets/components/${c.name}.svg`,
})),
frames: frames.map(f => ({
id: f.id,
name: f.name,
dimensions: { width: f.width, height: f.height },
exportedAs: `./assets/frames/${f.name}@2x.png`,
})),
importedAt: new Date().toISOString(),
};
await fs.writeFile(
'./.figma/metadata.json',
JSON.stringify(metadata, null, 2)
);
NPM Script (package.json):
{
"scripts": {
"figma:import": "tsx scripts/figma-import.ts",
"figma:import:watch": "tsx scripts/figma-import.ts --watch",
"figma:sync": "tsx scripts/figma-sync.ts"
}
}
CLI with Arguments:
// scripts/figma-import.ts
import yargs from 'yargs';
const argv = yargs(process.argv.slice(2))
.option('file-key', {
type: 'string',
description: 'Figma file key',
required: true,
})
.option('components', {
type: 'boolean',
description: 'Import components',
default: true,
})
.option('assets', {
type: 'boolean',
description: 'Export assets',
default: true,
})
.option('tokens', {
type: 'boolean',
description: 'Extract design tokens',
default: true,
})
.option('format', {
type: 'string',
description: 'Export format (svg, png, jpg)',
default: 'svg',
})
.parseSync();
const importer = new FigmaImporter({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
fileKey: argv.fileKey,
});
if (argv.components) {
const components = await importer.fetchComponents();
console.log(`Importing ${components.length} components...`);
}
if (argv.assets) {
// Export logic
}
if (argv.tokens) {
const tokens = await importer.extractDesignTokens();
// Save tokens
}
Usage:
# Import everything
npm run figma:import -- --file-key ABC123XYZ456
# Import only components
npm run figma:import -- --file-key ABC123XYZ456 --no-assets --no-tokens
# Export as PNG
npm run figma:import -- --file-key ABC123XYZ456 --format png
Rate Limits (Figma API):
Implement Caching:
import NodeCache from 'node-cache';
class CachedFigmaImporter extends FigmaImporter {
private cache: NodeCache;
constructor(config: FigmaConfig) {
super(config);
this.cache = new NodeCache({ stdTTL: 3600 }); // 1 hour TTL
}
async fetchFile() {
const cacheKey = `file:${this.fileKey}`;
const cached = this.cache.get(cacheKey);
if (cached) {
console.log('Using cached file data');
return cached;
}
const file = await super.fetchFile();
this.cache.set(cacheKey, file);
return file;
}
}
Rate Limiting:
import Bottleneck from 'bottleneck';
class RateLimitedFigmaImporter extends FigmaImporter {
private limiter: Bottleneck;
constructor(config: FigmaConfig) {
super(config);
// 30 requests per minute
this.limiter = new Bottleneck({
minTime: 2000, // 2s between requests
maxConcurrent: 1,
});
}
async fetchFile() {
return this.limiter.schedule(() => super.fetchFile());
}
async exportImages(...args: any[]) {
return this.limiter.schedule(() => super.exportImages(...args));
}
}
Common API Errors:
Error Handling Pattern:
async function importWithRetry(fileKey: string, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const importer = new FigmaImporter({
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
fileKey,
});
return await importer.fetchFile();
} catch (error) {
if (axios.isAxiosError(error)) {
const status = error.response?.status;
if (status === 401) {
throw new Error('Invalid Figma access token');
}
if (status === 403) {
throw new Error('No permission to access this file');
}
if (status === 429) {
const retryAfter = error.response?.headers['retry-after'] || 60;
console.log(`Rate limited. Retrying after ${retryAfter}s...`);
await sleep(retryAfter * 1000);
continue;
}
if (status === 500 && i < retries - 1) {
console.log(`Server error. Retrying (${i + 1}/${retries})...`);
await sleep(2000);
continue;
}
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
Import Figma designs with production-ready automation!