🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

openfig-core

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openfig-core

Isomorphic .fig file parser — reads Figma binary format in Node.js and browsers

latest
Source
npmnpm
Version
0.3.7
Version published
Maintainers
1
Created
Source

openfig-core

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.

Install

npm install openfig-core

Quick start

import { 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)`);
}

Browser

const resp = await fetch('/design.fig');
const data = new Uint8Array(await resp.arrayBuffer());
const doc = parseFig(data);

API

parseFig(data: Uint8Array): FigDocument

Parse a complete .fig ZIP archive. Extracts canvas.fig, meta.json, thumbnail.png, and images/*.

parseFigBinary(data: Uint8Array): FigDocument

Parse raw canvas.fig binary data (the blob inside the ZIP). Use this if you extract the ZIP yourself.

nodeId(node: FigNode): string | null

Format a node's GUID as "sessionID:localID" (e.g. "1:127"). Returns null if the node has no GUID.

FigDocument

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
}

FigNode

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
}

Encoding (write .fig files)

Round-trip: open, edit, save

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 download

From scratch: create a new .fig file

import { 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 });

encodeFigParts(doc: FigDocument): EncodedFigParts

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).

assembleCanvasFig(input: AssembleCanvasFigInput): Uint8Array

Assembles the canvas.fig binary from pre-compressed chunks. Format: [prelude 8B][version uint32 LE][len+chunk0][len+chunk1][len+chunk2+]...

createFigZip(input: CreateFigZipInput): Uint8Array

Packages canvas.fig + optional meta.json, thumbnail.png, and images/* into a .fig ZIP archive (store mode).

createEmptyFigDoc(): FigDocument

Creates an empty FigDocument with a bundled kiwi schema, a Document node, and a Page node. Ready for adding content and encoding.

File format documentation

Detailed documentation of the Figma binary format is in docs/:

DocCovers
Archive structureZIP layout, canvas.fig binary, kiwi schema, encoding pipeline
NodesNode types, GUIDs, parentIndex, hierarchy
ShapesROUNDED_RECTANGLE, FRAME, transforms, geometry
VectorVECTOR nodes, commandsBlob format, blob resolution, helper API
TextTEXT nodes, styles, fonts
ImagesImage storage, SHA-1 hashing, thumbnails
ColorsColor variables, palette
OverridesSymbol overrides (text, image, nested)
SlidesSlide dimensions, cloning, ordering
ModesSlides mode vs Design mode
InvariantsHard rules and sentinel values
ResearchBinary format analysis and references

Dependencies

  • fflate — ZIP extraction + deflate decompression
  • kiwi-schema — Kiwi binary format decoding
  • fzstd — Zstandard decompression

License

MIT

Disclaimer

Figma is a trademark of Figma, Inc. This project is not affiliated with, endorsed by, or sponsored by Figma, Inc.

Keywords

figma

FAQs

Package last updated on 22 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts