MCPInstall

figma-make-extractor

Extract source code, design tokens, and assets from Figma Make (.make) files into runnable React applications

Installs
116
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/design-skills --skill figma-make-extractor
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads figma-make-extractor automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Figma Make Extractor

Skill by ara.so — Design Skills collection.

Extract source code, design tokens, and assets from Figma Make (.make) files. Figma Make is an AI-powered prototyping tool that generates complete React applications. Unlike traditional Figma Design files (.fig), Make files contain React/TypeScript source code, design tokens, assets, and AI chat history - but they're not accessible via the Figma API.

This tool reverse-engineers the .make file format to extract all project contents into a runnable React application.

Installation

git clone https://github.com/albertsikkema/figma-make-extractor.git
cd figma-make-extractor/make_extraction

No global installation needed - the run-all.sh script handles dependency installation automatically.

Quick Start

cd make_extraction
./run-all.sh ../YourFile.make
cd output/react_app
npm install --legacy-peer-deps
npm run dev

This extracts everything and creates a ready-to-run React app at output/react_app/.

File Structure

A .make file is a ZIP archive containing:

YourFile.make (ZIP)
├── canvas.fig          # Binary file with source code & design data
├── meta.json           # Project metadata
├── ai_chat.json        # AI conversation history
├── thumbnail.png       # Preview image
├── images/             # Image assets
│   ├── [hash1]
│   └── [hash2]
└── blob_store/         # Additional binary data

The canvas.fig file uses a custom binary format:

  • Header: fig-makee magic bytes + padding (12 bytes)
  • Chunk 1: Deflate-compressed Kiwi schema (4 bytes size + data)
  • Chunk 2: Zstandard-compressed message data (4 bytes size + data)

Key Scripts

All scripts are in the make_extraction/ directory:

Master Script

# Extract everything and create React app
./run-all.sh <path-to-make-file>

# Clean up all generated files
./cleanup.sh

Individual Scripts

# 1. Decode canvas.fig binary to JSON
node 01-decode-canvas.js

# 2. Extract source code files
node 02-extract-source-code.js

# 3. Extract design tokens (colors, fonts, CSS vars)
node 03-extract-design-tokens.js

# 4. Create runnable React app
node 04-create-react-app.js

Programmatic Usage

Decoding canvas.fig

const fs = require('fs');
const pako = require('pako');
const { decompress } = require('fzstd');
const kiwi = require('kiwi-schema');

// Read binary file
const buffer = fs.readFileSync('output/extracted/canvas.fig');

// Skip 12-byte header (fig-makee + padding)
let offset = 12;

// Read Chunk 1 (schema) - deflate compressed
const chunk1Size = buffer.readUInt32LE(offset);
offset += 4;
const chunk1Data = buffer.slice(offset, offset + chunk1Size);
offset += chunk1Size;
const schemaBytes = pako.inflate(chunk1Data);

// Read Chunk 2 (data) - zstandard compressed
const chunk2Size = buffer.readUInt32LE(offset);
offset += 4;
const chunk2Data = buffer.slice(offset, offset + chunk2Size);
const messageBytes = decompress(chunk2Data);

// Decode using Kiwi schema
const schema = kiwi.compileSchema(schemaBytes);
const decoded = schema.decodeMessage(messageBytes);

// Handle BigInt in JSON
const json = JSON.stringify(decoded, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
, 2);

fs.writeFileSync('decoded-message.json', json);

Extracting Source Files

const fs = require('fs');

const decoded = JSON.parse(fs.readFileSync('decoded-message.json', 'utf8'));
const outputDir = 'source_files';
fs.mkdirSync(outputDir, { recursive: true });

let fileCount = 0;

function traverse(node) {
  if (!node) return;
  
  // Extract CODE_FILE nodes with source code
  if (node.type === 'CODE_FILE' && node.sourceCode) {
    const fileName = node.name || `file_${fileCount++}`;
    const filePath = `${outputDir}/${fileName}`;
    fs.writeFileSync(filePath, node.sourceCode, 'utf8');
    console.log(`Extracted: ${fileName}`);
  }
  
  // Traverse children
  if (node.children) {
    node.children.forEach(child => traverse(child));
  }
}

traverse(decoded);

Extracting Design Tokens

const fs = require('fs');
const path = require('path');

const sourceDir = 'source_files';
const tokens = {
  colors: { hex: new Set(), rgba: new Set(), hsl: new Set() },
  fonts: new Set(),
  cssVariables: new Set()
};

// Regex patterns
const hexPattern = /#[0-9A-Fa-f]{6}(?:[0-9A-Fa-f]{2})?/g;
const rgbaPattern = /rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+(?:\s*,\s*[\d.]+)?\s*\)/g;
const hslPattern = /hsla?\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%(?:\s*,\s*[\d.]+)?\s*\)/g;
const fontPattern = /font-family:\s*["']([^"']+)["']/g;
const cssVarPattern = /--[\w-]+/g;

// Scan all source files
fs.readdirSync(sourceDir).forEach(file => {
  const content = fs.readFileSync(path.join(sourceDir, file), 'utf8');
  
  content.match(hexPattern)?.forEach(c => tokens.colors.hex.add(c));
  content.match(rgbaPattern)?.forEach(c => tokens.colors.rgba.add(c));
  content.match(hslPattern)?.forEach(c => tokens.colors.hsl.add(c));
  
  let fontMatch;
  while ((fontMatch = fontPattern.exec(content)) !== null) {
    tokens.fonts.add(fontMatch[1]);
  }
  
  content.match(cssVarPattern)?.forEach(v => tokens.cssVariables.add(v));
});

// Convert Sets to Arrays
const result = {
  colors: {
    hex: Array.from(tokens.colors.hex),
    rgba: Array.from(tokens.colors.rgba),
    hsl: Array.from(tokens.colors.hsl)
  },
  fonts: Array.from(tokens.fonts),
  cssVariables: Array.from(tokens.cssVariables)
};

fs.writeFileSync('design-tokens.json', JSON.stringify(result, null, 2));

Creating React App Structure

const fs = require('fs');
const path = require('path');

const sourceDir = 'source_files';
const appDir = 'react_app';
const srcDir = path.join(appDir, 'src');

// Create directory structure
fs.mkdirSync(path.join(srcDir, 'components/ui'), { recursive: true });
fs.mkdirSync(path.join(srcDir, 'hooks'), { recursive: true });
fs.mkdirSync(path.join(srcDir, 'lib'), { recursive: true });
fs.mkdirSync(path.join(srcDir, 'data'), { recursive: true });
fs.mkdirSync(path.join(srcDir, 'styles'), { recursive: true });
fs.mkdirSync(path.join(appDir, 'public'), { recursive: true });

// Organize files by type
fs.readdirSync(sourceDir).forEach(file => {
  let targetDir = srcDir;
  const content = fs.readFileSync(path.join(sourceDir, file), 'utf8');
  
  // Categorize by content patterns
  if (file.startsWith('use-') && file.endsWith('.ts')) {
    targetDir = path.join(srcDir, 'hooks');
  } else if (file === 'utils.ts' || file === 'cn.ts') {
    targetDir = path.join(srcDir, 'lib');
  } else if (content.includes('@radix-ui') || content.includes('shadcn')) {
    targetDir = path.join(srcDir, 'components/ui');
  } else if (file.endsWith('.tsx')) {
    targetDir = path.join(srcDir, 'components');
  } else if (file.endsWith('.css')) {
    targetDir = path.join(srcDir, 'styles');
  }
  
  // Fix import paths
  let fixedContent = content
    .replace(/@radix-ui\/[\w-]+@[\d.]+/g, match => match.split('@')[0] + '@' + match.split('@')[1])
    .replace(/from\s+['"]\.\.\/[\w-]+@[\d.]+\//g, 'from "@/')
    .replace(/from\s+['"]figma:asset:([^'"]+)['"]/g, (match, hash) => {
      return `from "/images/${hash}.png"`;
    });
  
  fs.writeFileSync(path.join(targetDir, file), fixedContent, 'utf8');
});

Creating package.json

const packageJson = {
  "name": "figma-make-app",
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "framer-motion": "^11.0.0",
    "lucide-react": "latest"
  },
  "devDependencies": {
    "@types/react": "^18.3.1",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.4",
    "typescript": "^5.6.2",
    "vite": "^6.0.1",
    "@tailwindcss/postcss": "^4.0.0"
  }
};

fs.writeFileSync(
  path.join(appDir, 'package.json'),
  JSON.stringify(packageJson, null, 2)
);

Common Patterns

Verifying .make File Format

# Check if file is ZIP
xxd -l 4 YourFile.make
# Should show: 504b 0304 (PK..)

# Check canvas.fig header
unzip -q YourFile.make
xxd -l 20 canvas.fig
# Should show: 6669 672d 6d61 6b65 65 (fig-makee)

Converting Colors

// RGBA float (0-1) to hex
function rgbaToHex(color) {
  const r = Math.round(color.r * 255);
  const g = Math.round(color.g * 255);
  const b = Math.round(color.b * 255);
  return `#${r.toString(16).padStart(2,'0')}${g.toString(16).padStart(2,'0')}${b.toString(16).padStart(2,'0')}`.toUpperCase();
}

// Example usage
const figmaColor = { r: 0.11764705926179886, g: 0.11764705926179886, b: 0.11764705926179886, a: 1 };
console.log(rgbaToHex(figmaColor)); // #1E1E1E

Handling Image Assets

const fs = require('fs');
const path = require('path');

// Copy images from extracted .make to React app public folder
const imagesSourceDir = 'output/extracted/images';
const imagesTargetDir = 'output/react_app/public/images';

fs.mkdirSync(imagesTargetDir, { recursive: true });

fs.readdirSync(imagesSourceDir).forEach(hash => {
  const sourceFile = path.join(imagesSourceDir, hash);
  const targetFile = path.join(imagesTargetDir, `${hash}.png`);
  fs.copyFileSync(sourceFile, targetFile);
});

Output Structure

After running all scripts, the output/ directory contains:

output/
├── extracted/                  # Unzipped .make contents
│   ├── canvas.fig
│   ├── images/
│   ├── meta.json
│   └── ai_chat.json
├── decoded-message.json        # Decoded canvas.fig
├── source_files/               # Raw extracted source files
├── design-tokens.json          # Colors, fonts, CSS vars
└── react_app/                  # Ready-to-run React app
    ├── package.json
    ├── vite.config.ts
    ├── tsconfig.json
    ├── index.html
    ├── public/
    │   └── images/
    └── src/
        ├── main.tsx
        ├── App.tsx
        ├── components/
        │   └── ui/             # shadcn/ui components
        ├── hooks/
        ├── lib/
        ├── data/
        └── styles/

Troubleshooting

"Invalid stored block lengths" error

The data chunk uses Zstandard compression, not zlib/deflate. Install the correct package:

npm install fzstd

BigInt serialization error

Use a custom JSON replacer to handle BigInt values:

JSON.stringify(decoded, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
, 2);

Empty source code extraction

Verify the node type and property:

if (node.type === 'CODE_FILE' && node.sourceCode) {
  // Extract here
}

Not all nodes have source code - only CODE_FILE nodes with the sourceCode property.

Import path errors in React app

The 04-create-react-app.js script automatically fixes:

  • Version numbers in package names: @radix-ui/react-dialog@1.1.6 → @radix-ui/react-dialog
  • Figma asset imports: figma:asset:hash → /images/hash.png
  • Relative paths: ../component → @/components/component

If imports still fail, manually check the generated vite.config.ts has the alias:

resolve: {
  alias: {
    '@': '/src'
  }
}

npm install fails with peer dependency conflicts

Use the --legacy-peer-deps flag:

npm install --legacy-peer-deps

This is common with React 18 and older package versions.

Tailwind CSS PostCSS error

The generated app uses Tailwind v4 with @tailwindcss/postcss. Ensure postcss.config.js exists:

export default {
  plugins: {
    '@tailwindcss/postcss': {}
  }
};

Animations not working (React StrictMode)

The generated main.tsx omits React.StrictMode to avoid timer/animation issues with Framer Motion. If you need StrictMode, wrap the app manually but expect animation glitches.

Node Types Reference

When traversing the decoded message, you'll encounter these node types:

TypeDescriptionHas sourceCode
DOCUMENTRoot document nodeNo
CANVASPage/canvas containerNo
CODE_LIBRARYCode library containerNo
CODE_FILESource code file (.tsx, .ts, .css)Yes
CODE_COMPONENTExported React component definitionNo
CODE_INSTANCEInstance of a componentNo
FRAMEVisual frame (preview)No
RESPONSIVE_SETResponsive breakpoint containerNo

Only extract source code from CODE_FILE nodes.

API Reference

This is a CLI tool, not a library. All functionality is accessed via scripts:

# Full extraction pipeline
./run-all.sh <path-to-make-file>

# Individual steps (after running run-all.sh or extracting manually)
node 01-decode-canvas.js
node 02-extract-source-code.js
node 03-extract-design-tokens.js
node 04-create-react-app.js

# Cleanup
./cleanup.sh

Environment Variables

No environment variables required. All paths are relative to the script locations.

License

MIT License - see the repository for full license text.

Disclaimer

This tool documents a reverse-engineering process for educational purposes. The extracted code remains subject to Figma's terms of service and any applicable licenses. Use responsibly.

FAQ

What does the figma-make-extractor skill do?

Extract source code, design tokens, and assets from Figma Make (.make) files into runnable React applications

How do I install figma-make-extractor?

Run npx skills add https://github.com/reason-machines/design-skills --skill figma-make-extractor in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/figma-make-extractor/ (personal) or .claude/skills/figma-make-extractor/ (project).

Which AI agents can use figma-make-extractor?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is figma-make-extractor safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains figma-make-extractor?

It is published by reason-machines in reason-machines/design-skills. It has 116 recorded installs.

More from reason-machines/design-skills

View all →

awesome-claude-design

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

205 installs

open-design-ai-prototyping

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

174 installs

design-md-format

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

171 installs

open-codesign-ai-design

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

171 installs

design-md-chrome-extractor

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

166 installs

claude-design-system-hooks

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

Frontend & Design165 installs

Popular frontend & design skills

View category →

setup-matt-pocock-skills

mattpocock/skills

Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.

Frontend & Design1.1M installs

frontend-design

anthropics/skills

Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.

OfficialFrontend & Design1M installs

hyperframes-animation

heygen-com/hyperframes

All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.

prototype

mattpocock/skills

Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.

Frontend & Design999.2K installs

hyperframes-core

heygen-com/hyperframes

The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.

Frontend & Design930.5K installs

vercel-react-best-practices

vercel-labs/agent-skills

React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.

OfficialFrontend & Design843.1K installs