Import Figma designs into your project using Figma REST API or MCP server integration.
Import Figma designs into your project using REST API or MCP server integration. Use this to automate asset exports, extract design tokens, and sync components when starting projects or maintaining design systems.
/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!