Isomorphic .fig file parser — reads Figma binary format in Node.js and browsers.
Parses .fig (Figma Design), .deck (Figma Slides), and .jam (FigJam) files into a traversable node tree. Zero Node.js dependencies — works in browsers via bundlers.
npm install openfig-coreimport { parseFig, nodeId } from 'openfig-core';
import { readFileSync } from 'fs';
const data = new Uint8Array(readFileSync('design.fig'));
const doc = parseFig(data);
console.log(doc.header); // { prelude: 'fig-kiwi', version: 52 }
console.log(doc.nodes.length); // number of nodes in the file
// Traverse the node tree
for (const node of doc.nodes) {
const id = nodeId(node);
const children = doc.childrenMap.get(id) ?? [];
console.log(`${id} ${node.type} "${node.name}" (${children.length} children)`);
}const resp = await fetch('/design.fig');
const data = new Uint8Array(await resp.arrayBuffer());
const doc = parseFig(data);Parse a complete .fig ZIP archive. Extracts canvas.fig, meta.json, thumbnail.png, and images/*.
Parse raw canvas.fig binary data (the blob inside the ZIP). Use this if you extract the ZIP yourself.
Format a node's GUID as "sessionID:localID" (e.g. "1:127"). Returns null if the node has no GUID.
interface FigDocument {
header: { prelude: string; version: number };
nodes: FigNode[]; // all nodes (flat array)
nodeMap: Map<string, FigNode>; // id → node
childrenMap: Map<string, FigNode[]>; // parent id → children
schema: any; // decoded kiwi binary schema
compiledSchema: any; // compiled schema (encodeMessage/decodeMessage)
rawChunks: Uint8Array[]; // raw length-prefixed binary chunks
message: any; // full decoded kiwi message
meta?: Record<string, any>; // meta.json contents
thumbnail?: Uint8Array; // thumbnail.png bytes
images: Map<string, Uint8Array>; // filename → image bytes
}interface FigNode {
guid: FigGuid;
type: string; // FRAME, TEXT, ELLIPSE, SYMBOL, INSTANCE, ...
name: string;
phase?: string; // CREATED, REMOVED, etc.
parentIndex?: { guid: FigGuid; position: string };
size?: { x: number; y: number };
transform?: { m00, m01, m02, m10, m11, m12: number };
fillPaints?: FigPaint[];
textData?: { characters: string };
[key: string]: any; // open for all kiwi-decoded fields
}import { parseFig, encodeFigParts, assembleCanvasFig, createFigZip } from 'openfig-core';
const doc = parseFig(data);
// Edit nodes...
doc.message.nodeChanges[2].name = "Renamed";
const parts = encodeFigParts(doc);
// Caller must zstd-compress the message (openfig-core stays isomorphic)
const messageCompressed = yourZstdCompress(parts.messageRaw, 3);
const canvasFig = assembleCanvasFig({
prelude: parts.prelude,
version: parts.version,
schemaCompressed: parts.schemaCompressed,
messageCompressed,
passThrough: parts.passThrough,
});
const figZip = createFigZip({
canvasFig,
meta: doc.meta,
thumbnail: doc.thumbnail,
images: doc.images,
});
// figZip is a Uint8Array — write to disk or trigger downloadimport { createEmptyFigDoc, encodeFigParts, assembleCanvasFig, createFigZip } from 'openfig-core';
const doc = createEmptyFigDoc();
// Add nodes to doc.message.nodeChanges...
const parts = encodeFigParts(doc);
const messageCompressed = yourZstdCompress(parts.messageRaw, 3);
const canvasFig = assembleCanvasFig({ ...parts, messageCompressed });
const figZip = createFigZip({ canvasFig, meta: doc.meta, thumbnail: doc.thumbnail });Encodes a FigDocument into kiwi binary parts. Returns the raw message (caller must zstd-compress for chunk 1) and the deflate-compressed schema (chunk 0).
Assembles the canvas.fig binary from pre-compressed chunks. Format: [prelude 8B][version uint32 LE][len+chunk0][len+chunk1][len+chunk2+]...
Packages canvas.fig + optional meta.json, thumbnail.png, and images/* into a .fig ZIP archive (store mode).
Creates an empty FigDocument with a bundled kiwi schema, a Document node, and a Page node. Ready for adding content and encoding.
Detailed documentation of the Figma binary format is in docs/:
| Doc | Covers |
|---|---|
| Archive structure | ZIP layout, canvas.fig binary, kiwi schema, encoding pipeline |
| Nodes | Node types, GUIDs, parentIndex, hierarchy |
| Shapes | ROUNDED_RECTANGLE, FRAME, transforms, geometry |
| Vector | VECTOR nodes, commandsBlob format, blob resolution, helper API |
| Text | TEXT nodes, styles, fonts |
| Images | Image storage, SHA-1 hashing, thumbnails |
| Colors | Color variables, palette |
| Overrides | Symbol overrides (text, image, nested) |
| Slides | Slide dimensions, cloning, ordering |
| Modes | Slides mode vs Design mode |
| Invariants | Hard rules and sentinel values |
| Research | Binary format analysis and references |
fflate— ZIP extraction + deflate decompressionkiwi-schema— Kiwi binary format decodingfzstd— Zstandard decompression
MIT
Figma is a trademark of Figma, Inc. This project is not affiliated with, endorsed by, or sponsored by Figma, Inc.