The .design file format
A summary of the .design file: one JSON object with a tree and a write-append list of events, an assets folder, folding, recovery and versions.
A design is one UTF-8 JSON file with the extension .design, kept next to the code it describes. Everything that edits it, the editor, the ud command, a use_figma script, an agent or a person in a text editor, writes the same events into the same file, so a design can be reviewed, diffed, merged and versioned with git like any other source.
This page is a summary. The full specification is FMT-001; the JSON Schema is published at schema/design-1.0.schema.json (in the repository: schemas/design-1.0.schema.json), and ud validate checks a file against it.
Two parts: the tree and the WAL#
- The tree is the document as one JSON object, pretty printed. Only consolidation rewrites it.
- The WAL (write-append list) is the list of events since the last consolidation, one event per line, each line starting with a comma.
Opening a file reads the tree and folds the events into it in file order. Saving a change appends lines and touches no other line, so the diff of an edit is a few added lines that each name their actor, time, operation and target.
Property names, enumeration values and value shapes are those of the Figma Plugin API wherever that is sensible (layoutMode, itemSpacing, fills, characters, boundVariables, reactions …), so an agent that knows Figma can read the file without a new vocabulary. The format departs from Figma only where git or determinism requires it: nodes are a flat map with parent and a fractional index instead of children arrays, instance sublayers and auto layout positions are derived rather than stored, and image hashes are SHA-256.
The top-level object#
{
"$schema": "https://raw.githubusercontent.com/arthurafarias/vscode-extension-ui-designer/master/schemas/design-1.0.schema.json",
"$comment": "SoftForge UI Designer design file (FMT-001). Do not edit `tree`. To change the design, append one event per line to `wal`, starting the line with a comma, or use the `ud` command.",
"designVersion": "1.0",
"tree": { },
"wal": [
{"seq":0,"op":"wal.base","epoch":"ep-0c2e4a6b8d1f","treeHash":"sha256:…"}
]
}| Member | Meaning |
|---|---|
$schema |
URL of the JSON Schema of this format version, for completion and validation in editors. |
$comment |
Instructions for agents and people who open the file as text. |
designVersion |
Format version, "<major>.<minor>"; read before anything else. |
tree |
The consolidated document: document and pages, the node map, styles, variables, libraries, flows, comments, named versions and asset metadata. |
wal |
The wal.base marker followed by the events since the last consolidation. |
Members a reader does not know are kept and written back unchanged. An empty file opens as a blank design with one page.
Events#
Every event is a self-contained line: an envelope (seq, at, by, op, optional group and note) and a payload, for example
,{"seq":14,"at":"2026-10-06T09:20:04.118Z","by":"Ada","op":"node.update","id":"nd-4c6e8a0b2d1f","set":{"paddingTop":48}}An event carries every value it generated (ids, times, computed results), so folding the same file gives the same document on every surface. Events of one transaction share a group, which is also one undo step. Identifiers have a type prefix (doc-, pg-, nd-, vs-, …).
Folding and merging#
Folding applies events in file order, last in file wins, per property: edits of different properties of one node on two branches both survive. Because every change only appends lines, *.design merge=union in .gitattributes keeps the lines of both branches; the reader then reports real conflicts (the same property changed on both sides), re-attaches orphans and resolves fractional-index ties. A tree hash identifies the folded state; ud commands accept --if-version <hash> to refuse a change when the file moved underneath.
Consolidation#
The WAL grows by one line per change. Consolidation (ud consolidate) folds it into a new tree, shortens long fractional keys, drops derived values, defaults and unused metadata, and leaves the WAL with a fresh wal.base marker. The state hash is unchanged. Individual events are lost to the History panel after consolidation; earlier history comes from git and from named versions, which are kept.
The assets folder#
Images and fonts are not stored in the file. They live in <name>.design.assets/ next to it, each named by the SHA-256 of its bytes (sha256-<hex>.png), written before any event refers to them and stored once however often they are used. Fonts are embedded only when their licence allows it (ud font embed). Renaming or moving a design in the editor moves the folder with it.
Tolerant reading and recovery#
A design file is edited by many writers, so a reader repairs what it can and reports it instead of refusing the file: missing pages or properties get defaults, unknown members and operations are kept, a node of an unknown type renders as a placeholder, and a file broken by a bad manual merge (not valid JSON any more) is recovered line by line. Warnings appear in the editor and in ud validate.
Versions and migrations#
designVersion decides how a file is opened: the current version is read; a newer minor version of the same major is read and edited tolerantly, with a note that some content may show as placeholders; an older major version is migrated in memory and rewritten in the new format with the next change, after a confirmation; a newer major version is refused for editing, with “Open as Text” offered. See FMT-001 §10.