Figma I/O — @grida/io-figma
SkillMediaGuides your agent in working on the Figma I/O package, covering parsing and converting Figma files to Grida formats.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Figma I/O — @grida/io-figma skill
About this capability
Guides work on the Figma I/O package (@grida/io-figma, packages/grida-canvas-io-figma/). Covers the fig-kiwi binary parser, Kiwi→REST→Grida conversion pipeline, fig2grida CLI, REST API JSON conversion, and testing with clipboard/fig/REST fixtures. Use when adding node type support, fixing conversion
What this skill tells your AI
The instructions your AI receives, as published by gridaco/grida in .agents/skills/io-figma/SKILL.md and read by ahel’s review.
Package: packages/grida-canvas-io-figma/
Architecture
.fig bytes / HTML clipboard
→ fig-kiwi parser (fig-kiwi/) low-level, zero-opinion
→ NodeChange[] (Kiwi schema types)
→ iofigma.fromKiwi*() (lib.ts) Kiwi → Grida node
Figma REST API JSON
→ iofigma.fromRest*() (lib.ts) REST → Grida node
Orchestration:
fig2grida-core.ts — browser-safe: input detection, page loop, pack
fig2grida.ts — CLI wrapper (Node.js only, uses fs + process.argv)
Key invariant: The Kiwi path converts to REST format first (Kiwi → REST → Grida). lib.ts is the single source of truth for node conversion; it does not know the input origin.
Output: Grida format (.grida ZIP — FlatBuffers + images). See io-grida skill for format details, Rust loading, and round-trip testing.
fig2grida Input Formats
fig2grida(input) in fig2grida-core.ts auto-detects the input:
| Input | Detection | Path |
|---|---|---|
.fig bytes | ZIP without document.json, or raw Kiwi | fig-kiwi parser |
| REST archive ZIP | ZIP containing document.json (+ optional images/) | REST JSON path |
| REST JSON bytes | Starts with { | REST JSON path |
| REST JSON object | Non-Uint8Array object | REST JSON path |
The REST JSON path (extractCanvases) accepts multiple response shapes:
{ document: { type: "DOCUMENT", children: [CANVAS, …] } }— fullGET /v1/files/:key{ document: { type: "CANVAS", children: […] } }— single-page node fetch{ nodes: { "id": { document: … }, … } }—GET /v1/files/:key/nodes?ids=…{ type: "DOCUMENT", children: … }— document node directly{ type: "CANVAS", children: … }— single CANVAS node{ children: […] }— bare object with children
Public APIs (both in fig2grida-core.ts):
fig2grida(input, options?)→.gridaZIP bytes (Fig2GridaResult)restJsonToGridaDocument(json, options?)→ in-memoryDocument+ assets (no ZIP packing)
Key Files
| File | Role |
|---|---|
lib.ts | All iofigma.from* converters (Kiwi→REST and REST→Grida) |
fig2grida-core.ts | Orchestrator (.fig, REST JSON, REST ZIP) |
fig2grida.ts | CLI entry point (Node.js only) |
fig-kiwi/index.ts | Low-level parser public API |
fig-kiwi/blob-parser.ts | Vector network + commands blob decoding |
fig-kiwi/schema.ts | Kiwi type definitions (NodeChange, Message, …) |
References
| Path | What |
|---|---|
.ref/figma/ | Kiwi schema (fig.kiwi, fig.kiwi.d.ts), extraction tool (fig2kiwi.ts), Figma REST & Plugin API typings |
docs/wg/feat-fig/glossary/fig.kiwi.md | Deep-dive: node types, vector blob format, GROUP/FRAME detection, text/font mapping |
packages/grida-canvas-io-figma/README.md | Feature matrix, limitations, usage |
Common Tasks
Add support for a new Figma property
- Find the property in
fig-kiwi/schema.ts(Kiwi) or REST JSON infixtures/test-figma/. - Add mapping in
lib.tsunder the relevantiofigma.from*converter. - Add a test in
__tests__/against an existing fixture.
Debug a clipboard paste failure
Clipboard issues = Figma changed their Kiwi schema.
- Save the failing HTML clipboard as a fixture.
- Run
readHTMLMessage(html)→ inspect rawMessage. - Diff parsed
NodeChange[]againstfig-kiwi/schema.ts. - Update
schema.ts(field changes) orblob-parser.ts(blob layout changes).
Run fig2grida
pnpm --filter @grida/io-figma fig2grida input.fig
npx tsx packages/grida-canvas-io-figma/fig2grida.ts input.fig --pages 0,2
npx tsx packages/grida-canvas-io-figma/fig2grida.ts input.fig --info
Figma API token
figma_archive.py requires a Figma Personal Access Token. The script
checks FIGMA_TOKEN then X_FIGMA_TOKEN env vars, or accepts
--x-figma-token on the CLI. It fails fast with a clear error if none
is set.
The root .env file is not a standard part of this project — it may
not exist on every machine. Never read .env directly (for security
reasons). Instead, if a token is needed and not already in the
environment, ask the user to provide one and have them export it:
export FIGMA_TOKEN=figd_...
Create REST API fixtures
Use scripts/figma_archive.py. See the script header for full documentation, output layout, and --export behaviour.
python .agents/skills/io-figma/scripts/figma_archive.py \
--filekey <KEY> --archive-dir fixtures/test-figma/community/<name>
# With oracle PNGs (nodes must have export presets in Figma)
python .agents/skills/io-figma/scripts/figma_archive.py \
--filekey <KEY> --archive-dir fixtures/test-figma/rest-api/local/<name> --export
Refig — correctness testing against Figma's renderer
For end-to-end correctness of the Figma import pipeline (does our Grida
render of a Figma file match Figma's own render?), use the refig
flow: oracle PNGs from Figma's Images API + @grida/reftest (developed in
the engine repo: https://github.com/gridaco/nothing/tree/main/packages/grida-reftest)
for the diff/score/report. Suites live in the engine repo's gitignored local
corpus (fixtures/local/refig/<name>.<filekey>/ — local-only, machine-local
by definition). See the engine repo's render-reftest skill, section
"Figma — the refig reftest pipeline":
https://github.com/gridaco/nothing/blob/main/.agents/skills/render-reftest/SKILL.md.
When debugging a conversion bug with a visible visual symptom, run the
refig suite to locate the diverging nodes, then drill into lib.ts for
the specific node type or property.
Tests
pnpm --filter @grida/io-figma test # all
pnpm --filter @grida/io-figma test -- __tests__/iofigma.kiwi.test.ts # specific
| Test file | Covers |
|---|---|
iofigma.kiwi.test.ts | Kiwi clipboard → Grida |
iofigma.kiwi.fig.test.ts | .fig file parsing |
iofigma.kiwi.vector-network.test.ts | Vector network blob decoding |
iofigma.kiwi.clipboard-overrides.test.ts | Component instance overrides |
iofigma.kiwi.clipboard-components.test.ts | Component / instance clipboard |
iofigma.kiwi.clipboard-text-overrides.test.ts | Text style overrides |
iofigma.rest-api.no-geometry.test.ts | REST API (no geometry) |
iofigma.rest-api.vector.test.ts | REST API vector paths |
fig2grida.test.ts | End-to-end pipeline |
fig-kiwi/__tests__/ | Low-level parser units |
Fixtures: fixtures/test-figma/rest-api/ (committed REST JSON), fixtures/test-figma/community/ (archived files), fixtures/local/ (gitignored, manual testing).
Known Limitations
- Component sets, FigJam nodes (STICKY, CONNECTOR, TABLE) not supported
characterStyleOverrides/styleOverrideTablepartially mapped from Kiwi- Style/variable bindings not preserved
- Kiwi is undocumented — can break after Figma updates
Check the README's limitations section before writing new code. If lifting a limitation, update the README.
Verification
pnpm turbo typecheck --filter='./packages/grida-canvas-io-figma'
pnpm turbo test --filter='./packages/grida-canvas-io-figma'
Signals
- GitHub stars
- 3k
- Forks
- 148
- Last commit
- Sep 2026
ahel review
S4info
community integration — published by gridaco, not figma
Automated review, not a security audit. Ruleset v1.
Advanced
- Catalog kind
- skill
- Gateway key
io-figma- Source
- github.com/gridaco/grida