THE FIELD GUIDE

Projects & saving

Multiple forms, embedded assets, .forma files, and history.

The Builder saves designs as .forma. Open also accepts .frma. The extension belongs to Forma; internally the file is readable, versioned JSON with format: "forma-project". Single-form projects use version 1; multi-form projects use version 2 with a forms array. Projects containing custom files or folders use version 3 with forms and files arrays. Versions 1 and 2 still open.

  • Save: Ctrl+S or the File menu/toolbar. The first save asks for a location.
  • Save As: Ctrl+Shift+S or File > Save As.
  • Open: Ctrl+O or the File menu/toolbar.
  • An asterisk in the window title marks unsaved changes.
  • New Project, Open, and closing the window offer Save, Discard, or Cancel when needed.

Files retain the form title and size, stable control IDs and names, nesting and sibling order, control values, layout slots, appearance, spacing, layer order, and component configuration. Component CSS, script.js, custom JSON values, and the form's main.js source are embedded too. Components reopen stopped, in Design mode. Local images are embedded as data URLs on save so they survive moving the design or deleting the source image. Remote image URLs remain references.

Files describe the design and its values; they do not contain C# event handlers, background tasks, or a compiled application. JSON is plain text, not encrypted. Zoom, current selection, and transient Preview state are not saved. This includes shared modules, reactive refs, timers and values changed by runtime scripts. Every fresh Preview initializes these from the saved design and script source.

External editing files live in <project-name>.components/<control-id>/; the global file lives in <project-name>.components/<form-id>/global/. These folders are editing conveniences, not dependencies needed to reopen the project. See Custom Properties and global scripts for save/auto-apply and legacy file support.

Open validates the format version, control types, IDs, tree structure, and properties before replacing the active design. Unsupported versions and malformed files report an error. Saves write a temporary sibling file and replace the target only after writing successfully. Current limits are 50 MB per project, 10 MB per newly embedded image, 5,000 controls, and 32 nesting levels.

Windows file association and opening a design by double-clicking Explorer are future installer work. Use Open inside Builder for now.

Forms within a project

File → New form / Ctrl+N adds another form to the current project and keeps its path, existing forms and scripts. It is an undoable edit. Use the Project form selector above the canvas to switch forms; switching is not a design edit and does not mark a saved project dirty. Forms have unique names; their controls use names scoped to that form. Save/Open retains every form, its control tree, styles, component sources and images in one .forma file. Open selects the first form. A project supports up to 100 forms, with the existing total project limits applying across all forms.

File → New project / Ctrl+Shift+N starts a separate blank project and asks about unsaved work. Preview starts with the selected form and snapshots every form in the project. Adding a form does not create navigation automatically; use forma.showForm from a component script to open it.

The global script is shared project source, stored on the first form for legacy compatibility, and runs when previewing any form. Runtime state still belongs to one Preview run. Each window has its own controls, script scope and providers; serializable forma.shared values synchronize across the windows in that run. Component code tabs stay open when switching forms, and selecting a component's code tab returns to its owning form.

Open a form and receive its result

On a button in the first form:

forma.on("Click", async () => {
  const result = await forma.showForm("settingsForm", { modal: true });
  if (result?.saved) forma.set("statusLabel", "text", "Settings saved");
});

On the Save button in settingsForm:

forma.on("Click", () => forma.closeForm({ saved: true }));

Omit modal to open a separate nonmodal window. The promise resolves when that window closes; the native close button supplies no custom result. Forms are found by stable ID or case-insensitive form name. A missing form rejects the promise. Closing the first Preview window closes the other windows opened within its run. Designer edits made after Preview starts do not alter that run.

Recovery and local Builder data

Unsaved work is autosaved approximately every 30 seconds for crash recovery. After an abnormal exit, Builder offers the latest abandoned design: restore it, discard it, or cancel to keep the recovery for later. Restore marks the design unsaved; save it normally to keep it. Recovery supplements normal project saving.

Recent-project and recovery data use %LOCALAPPDATA%\\Forma by default. To choose another location, set this environment variable before starting Builder:

$env:FORMA_DATA_DIR = "C:\FormaData"
dotnet run --project src/Forma.Builder

This setting relocates Builder data, not your .forma projects. It does not automatically copy existing recent/recovery records into the new directory.

Custom project files

Explorer's Files branch supports folders and .js, .css, and .json files. Right-click the project, Files, or a custom folder to create a file/folder or add an existing file. Contents are embedded in .forma, so imported files remain available when the original file is moved or removed. Each file opens in its own editor tab with formatting, diagnostics and external editing support. JSON files may contain arrays or primitives; component Custom Properties still requires a JSON object.

Creating, renaming, removing and saving file contents participates in Undo/Redo. Names are unique within each folder, ignoring case. Files keep their extension when renamed. Folders and file IDs are independent of filesystem paths. Limits are 1000 file/folder entries, 200,000 characters per file and 32 nesting levels, within the existing 50 MB project limit. Removing a folder also removes its children; confirmation mentions discarded unsaved editor drafts. Undo restores the last applied project contents, not drafts that were never saved.

Custom files are stored project resources. They are not automatically executed, applied as styles, or registered as forma.use modules. Component script.js and main.js remain the execution entry points. Import .js or .json resources from those scripts with relative paths from the Files root. Imported JavaScript runs once per Preview; see module imports.

External editing copies use <project-name>.components/<first-form-id>/project-files/<file-id>/<file-name>. Renaming changes the project's canonical editing filename; an older editing copy may remain in that scratch folder. These folders are not required to reopen the project.

Undo and redo

Use Edit or the toolbar, Ctrl+Z for Undo, and Ctrl+Y or Ctrl+Shift+Z for Redo. Design history includes insertion, deletion (including descendants), movement, resizing, property changes, image selection, layer actions, and applied component or global script edits. Consecutive edits to the same Text field within 800 ms form one history entry. A new edit after Undo clears the redo branch. History retains up to 100 edits and trims older large entries.

Undo restores control IDs, properties, nesting, and selection. Save keeps history; New Project and Open start fresh histories; New Form is recorded in project history. Returning to the saved design clears the unsaved marker. History is for the current session and is not stored in the .forma file. Preview interactions are not recorded, and design Undo/Redo is disabled in Preview. When a text field has focus, Ctrl+Z uses its native text undo; click the canvas or the Edit/toolbar action to undo a design edit.

Search components, properties, and guides.

↑↓ navigate Enter open Esc close