Documentation

Everything tofig does, everything it doesn't, and what to do about the difference.

Figma Community Changelog Report an issue

Install

There are two pieces. Most people only need the first.

The Figma plugin

tofig is free on the Figma Community. On the plugin page, click Open in… and pick the file you want to use it in: Figma installs it and runs it there. There's no account, no API key and nothing to configure.

Already in a file? Right-click the canvas and choose Plugins, or click Actions in the toolbar, then search for tofig. Pressing Save on the Community page keeps it at the top of that list.

Works in the browser and in the desktop app, on any Figma plan. To run your own build instead, see Build from source.

The renderer CLI (optional)

Only needed for exports the plugin sandbox can't run. See External renderer for when that applies.

npm install -g @aca-so/tofig

Requires Node and a local Google Chrome or Chromium. puppeteer-core drives your existing browser rather than downloading one.

Quick start

  1. Open a Figma Design file, or a Figma Slides file.
  2. Run tofig: right-click the canvas → Pluginstofig.
  3. Paste your HTML, or drop a single .html file onto the window.
  4. Click Convert.

tofig reads figma.editorType and picks the output for you: frames in a Design file, slides in a Slides file. There is no mode switch.

Coming from Claude

The most common route in. Claude generates a design as an artifact, and you want it in Figma as layers rather than a flat screenshot. The full walkthrough is on its own page, including the export prompt and what to do when the artifact turns out to be a live app.

The short version: ask for the design as a single self-contained HTML file, with inline CSS, no CDN links, nothing rendered by JavaScript at runtime, images as data: URIs or inline SVG, and a font that exists in your Figma. Then paste that into the plugin.

Decks work the same way, and are one of the best cases. A Claude slide deck is a single HTML file with one section per slide. Run tofig in a Figma Slides file and each section arrives as its own slide, sized and scaled to fit, with its text still editable.

What tofig accepts

Self-contained HTML. Concretely, that means everything the page needs is in the file:

  • Inline <style> and style="" attributes
  • Images as data: / base64 URIs
  • Inline <svg>
  • Web fonts that already exist in your Figma

External URLs are never fetched. The plugin declares networkAccess: none in its manifest, so Figma blocks outbound requests entirely. A stylesheet or image behind https:// will simply be absent. This is deliberate: it's what makes the plugin safe to point at markup you didn't write.

What you get back

In your HTMLIn Figma
Block elementsFrames, clipped to their own bounds
Text nodesReal text layers: editable, restyleable
Inline <svg>Vector nodes with editable paths
data: imagesImage fills
CSS gradientsNative Figma gradient paints
Borders, radiiStrokes and corner radii
<section> in a deckOne slide, or one frame, per section

Layout is absolutely positioned. tofig optimises for visual fidelity (what you see is where it lands) rather than reconstructing auto-layout. Auto-layout inference is on the roadmap.

Fonts & substitution

Figma can only set type in fonts it already has. When your HTML asks for one that isn't available, tofig walks a fallback chain:

  1. The requested family at the requested weight and style.
  2. The nearest available weight in the same family.
  3. Inter.

Every substitution is reported back in the plugin UI after conversion, so you know exactly which layers drifted and why. If text looks slightly small after import, that's the fitting pass: a fallback font usually reflows taller, and since every layer is absolutely positioned, tofig shrinks text to stay inside its measured box rather than letting it overlap the element below.

External renderer

Some files with an .html extension are really applications. They boot a React runtime, pull React or Babel from a CDN, call eval, or read localStorage. Figma's plugin sandbox blocks all of that, so they cannot render inside the plugin.

For those, render on your own machine in an unrestricted Chrome and import the result. Same extractor, same output, just no sandbox.

# writes export.tofig.json next to the input
tofig-render export.html

# or without installing anything
npx --package=@aca-so/tofig tofig-render export.html

Then drop the resulting .tofig.json onto the plugin, or use Choose…. It imports directly, with no in-plugin render step.

Options

FlagDefaultWhat it does
--targetdesigndesign or slides
--width1440Viewport width used for the render
--outalongside inputOutput path for the JSON
--chromeauto-detectedPath to a specific Chrome binary
--headfuloffShow the browser, useful when debugging a render

Chrome is also picked up from TOFIG_CHROME or CHROME_PATH if either is set.

The .tofig.json file

A plain JSON import payload: the extracted layer tree, already resolved to geometry, styles and embedded image bytes. It's deterministic: the same input produces the same file, which makes it reviewable and diffable.

{
  "tofig": 1,
  "target": "slides",
  "title": "deck",
  "multiFrame": true,
  "roots": [ /* one entry per frame or slide */ ]
}

Because the payload carries its own bytes, the plugin never needs network access to import it. The security property is preserved end to end.

Troubleshooting

Nothing renders, or the result is blank

The export is probably a live app rather than static markup. Use the external renderer.

Images are missing

They're almost certainly external URLs. Inline them as data: URIs and re-export.

Text overlaps other text

A font substitution reflowed taller than the original. tofig fits text back into its measured box, but a very tight design can still crowd. Install the real font in Figma and re-run.

The type looks slightly smaller than the original

Expected, and deliberate: see Fonts & substitution. A marginally smaller glyph is the trade for no overlap.

A layer sticks out past its frame

Shouldn't happen as of 0.3.1, which clips each captured root to its own frame. If you see it, please open an issue with the HTML.

Build from source

The repo is public and MIT licensed.

git clone https://github.com/aca-so/tofig
cd tofig
npm install
npm run build        # produces code.js + ui.html

Then in the Figma desktop app: Plugins → Development → Import plugin from manifest… and pick this repo's manifest.json. Two sample files live in examples/: one for Design, one for Slides.