No repo code was changed for this research.
Full report (research notes, ~4,000 words)
Date: 2026-09-08. Scope: can a single board (mockups/canvases/<slug>/NN-*.html) or a whole canvas be turned into a Figma file, which tools exist for it, and whether any of those tools have source code we can reference. Research only; no repo code was changed.
Short answer
- Per-board export is feasible today, with editable layers, and was verified end to end. One board (
snapaction-ios/05-resource-detail.html) was pushed into Figma through the official Figma MCP server's capture tool. Result: 40 layers, 21 editable SF Pro text nodes, zero missing fonts, images as image fills, phone bezel and corner radius intact. See "Proof of concept" below.
- Whole-canvas export is feasible but nothing does it turnkey. Every tool works one page at a time. A canvas export is "capture each board, then place the frames per
layout.json", which is a script, not a product feature. A CC0 community skill, bulk-capture, already shows the parallel-capture half. tldraw's own SVG export does not help.
- There is no
.fig writer and no HTML import in Figma itself. Every route goes through the Figma Plugin API, in one of three wrappers: the official MCP server, the official Chrome extension, or a third-party plugin/extension.
- The official capture, the official Chrome extension and html.to.design are the same engine. Figma purchased divRIOTS's HTML-to-Figma technology in September 2025. The quality the team has seen from html.to.design is what the scripted route produces.
- None of that engine is open source, and neither is Figma's MCP server. What is public: Figma's skills and example scripts (
figma/mcp-server-guide), the Plugin API typings (MIT), a CC0 bulk-capture skill, and a set of smaller open-source HTML-to-Figma converters and MCP bridges, listed under "Source code availability".
- Recommended route: the official Figma MCP
generate_figma_design. It is free, already installed and authenticated in this Claude Code setup, scriptable, and it produced the best result. For a designer working by hand, the free official Chrome extension or html.to.design's File tab (drag the .html in) are the manual equivalents.
What a mockup is, and why it matters for import
Each board is a single self-contained HTML file: inline CSS with custom properties as tokens, images inlined as data: URIs, the system font stack (-apple-system / SF Pro), plain flex plus absolute positioning, no web fonts, no SVG, no scripts. Sizes run from 6 KB to 1.8 MB per board (04-collection.html is the largest); a whole canvas is 80 KB to 7 MB of HTML.
That is the easiest possible input for every HTML-to-Figma tool: no external resources to fetch, no auth, no JS state. The canvas renders boards in sandboxed srcdoc iframes inside tldraw, so capturing the canvas page itself is not useful; the unit of export is the board file, served on its own URL or dropped in as a file.
Ground truth about Figma's import surface
| Path |
Status |
| Native HTML import |
Does not exist. Figma imports only .fig, SVG, images, PDF (partial). |
.fig file written by a third party |
Proprietary Kiwi-based format, no public writer. Not an option. |
| REST API |
Read-only for design content (nodes, images, JSON). Cannot create layers. |
Plugin API (figma.createFrame, createText, createNodeFromSvg, image fills) |
The only write path. Everything below is a wrapper around it. |
| SVG import |
Native and editable, but only for vector content. Our boards are HTML/CSS, not SVG, and foreignObject is not imported. |
Routes, ranked for this repo
1. Official Figma MCP server, generate_figma_design (verified)
How it works: the MCP tool returns a capture ID and a capture script (https://mcp.figma.com/mcp/html-to-design/capture.js). The script runs inside the page, serializes the rendered DOM (computed styles, text runs, images) and POSTs it to Figma, which materialises it as layers in a target file. For local pages Figma expects you to add a <script> tag and open the page with a #figmacapture=... hash; for external pages it documents Playwright injection. Playwright injection works for local pages too, which is what the proof of concept used, so the committed board HTML never has to change.
Fit: excellent. Free with a Figma account (writing to non-draft files needs a Full seat, which the ReScience Lab Pro team has). Fully scriptable from Claude Code, one capture ID per board, and multiple capture IDs can run in parallel against one file. Limit: 50 MB per capture including serialized images, far above our largest board.
Caveats seen in the proof of concept:
- Output is raw frames, not components or styles. Figma's own guidance is to treat the capture as a pixel-perfect reference and rebuild with design-system components via
use_figma if you want instances. For a clone's screens, raw frames are the honest representation anyway.
- Only 1 of 19 frames came in with auto layout. That is faithful to the source, not an engine limit: the board's CSS positions every child of
.phone absolutely (.phone>*{position:absolute}) at measured coordinates, and the only flex container is body. Boards built this way will never yield auto layout from any converter; text reflow after edits will not push siblings.
- The board's
transform: scale(1.02575) on the phone shell was baked into geometry: font size 17.23 instead of 16.8, phone width 441.07. Consistent with what the canvas inspector reports, but the numbers are not "clean".
- Hairline dividers arrived as 1.03 px frames, icons as image fills (the boards use PNG icons), and the top region is one image because the board itself inlines
assets/art/05-shot.png there.
- No variables were bound because the target file had none. The capture supports binding to existing colour, number and string variables, so exporting into a file that already holds the canvas's token collection would link colours automatically. That is a possible follow-up:
00-design-tokens.html is the token board, and use_figma can create a variable collection from it.
2. Official Figma Chrome extension (free, first party, manual)
Figma now ships a first-party Chrome extension that captures a webpage or a selected element into Figma Design or FigJam as editable layers, binding existing variables where available. Same engine and same output as route 1 ("plain Figma layers with variables bound if any are available", not mapped to components or styles). Privileged chrome:// pages cannot be captured; canvas-rendered or heavily animated sites may not translate cleanly.
Fit: the manual counterpart of route 1. Serve a board folder (python3 -m http.server in the canvas folder, or the Vite dev server's /@fs/ path), open a board, click capture. One board per click. No scripting, so it does not scale to 180 boards, but it is the zero-setup answer for "I want this one screen in Figma now".
3. html.to.design by divRIOTS (plugin + Chrome extension, free tier)
The most established third-party tool. Three inputs: public URL, a Chrome extension that captures private or localhost pages and produces a .h2d file (or copies to the clipboard for direct paste), and a File tab that accepts .html, .htm, .zip, .mhtml and .h2d directly. Zip uploads are capped at 32 MB. The free plan includes local file upload, the extension, font mapping, auto layout and styles; PRO adds unlimited imports (1,000 per month fair use), bulk import of multiple files, and hi-res images.
Fit: good, and the team already has positive hands-on experience with it (Yilin reports its output quality is good). Because our boards are single files with no external resources, the File tab path is the simplest manual route of all: drag 05-resource-detail.html into the plugin. Bulk import (PRO) would take a whole canvas folder in one go, still without the row layout.
Relation to the official capture: they are the same engine. Figma purchased divRIOTS's HTML-to-Figma technology in September 2025 (see "Source code availability" below). The MCP capture script is served from mcp.figma.com/mcp/html-to-design/capture.js and carries h2d markers, html.to.design's own capture format. So the proof of concept above is, in effect, html.to.design's converter driven through Figma's API, and the quality you have seen from the plugin is what the scripted route produces. What the plugin still adds on top: its own UI for file, zip, .h2d and clipboard input, Figma style creation, and PRO bulk import. The auto layout difference I suspected earlier does not hold; the absolute frames came from the board's CSS.
4. html2design (plugin, paste HTML, 10 free imports then $12/month)
Newer competitor. Paste raw HTML plus CSS into the plugin, or give a public URL; no extension. Claims editable native layers including SVG vectors. Ten free manual imports, then paid.
Fit: workable because a board is one pasteable file, but it offers nothing the free routes above lack.
5. Builder.io (Visual Copilot plugin + Chrome extension)
Builder's original open-source figma-html importer is retired in favour of its Chrome extension, which captures a page or a selected DOM element (including localhost and authenticated pages) and imports it through the Builder.io Figma plugin. Builder states 80 to 90 percent accuracy. The @builder.io/html-to-figma npm package still exists but is no longer the supported path.
Fit: usable, but the product is oriented toward Figma-to-code and its own platform; no reason to prefer it.
6. Other Chrome extensions and plugins seen
- "Export to Figma" (exporttofigma.com): extension that copies a page or element and pastes into Figma as editable layers. Paid product, unverified.
- "Web to Figma": extension plus plugin, captures pages and components. Unverified.
- "HTML to Figma Importer": Figma plugin, version 2 released 2026-02-27 with
position: fixed support. Unverified.
- Anima "Buddy": accepts HTML, URL, image, and Claude artifacts; AI-interpreted output that its own comparison says needs cleanup.
- Codia and several "HTML to Figma" community plugins: screenshot-and-guess converters, lower fidelity, skipped.
Routes that do not work
- tldraw SVG or PNG export of the canvas. tldraw exports shapes it can draw; the
canvas-file shape has no toSvg, and an iframe cannot be flattened into SVG anyway. At best each board becomes a raster, which Figma would import as a flat image. Only tldraw-native shapes (arrows, notes, text) would export as vectors, and the layouts do not persist any of those; layout.json holds only rows, files, titles, labels and links.
- Figma REST API. Read-only for design content.
- Writing
.fig directly. No public format or writer.
- Screenshot import. Trivial and non-editable; the canvas already has thumbnails.
Proof of concept: what was actually done
- Served
mockups/canvases/snapaction-ios/ with python3 -m http.server 8765.
- Created a scratch Figma file "super-prototyping Figma export test" in the ReScience Lab team (the only plan with a Full seat): https://www.figma.com/design/cG2WJpjXX9YsDJhDiE5Ru8
- Called
generate_figma_design for a capture ID, then ran a 15-line Playwright script (headless Chromium, 478 by 980 viewport, 2x scale) that loaded the board, injected Figma's capture.js, and called window.figma.captureForDesign(...).
- Polled once; the capture completed. Frame: https://www.figma.com/design/cG2WJpjXX9YsDJhDiE5Ru8?node-id=1-2
Measured on the result (read-only Plugin API query):
| Metric |
Value |
| Layers |
40 (19 frames, 21 text) |
| Text font |
SF Pro Medium x18, SF Pro Bold x3 |
| Missing fonts |
0 |
| Fill types |
33 solid, 6 image |
| Auto-layout frames |
1 |
| Outer phone frame |
441.07 x 956.00, corner radius 56.4, clips content, 2 effects |
| Bound variables |
0 (file has none) |
Screenshot of the Figma frame, rendered by Figma: /tmp/figma-export-capture.png. It is visually indistinguishable from the board in the canvas.
Delete the scratch file if it is not wanted; it lives in the team's drafts. The scratch server and script were stopped after the capture.
What a whole-canvas export would take
Not built, per instruction. The shape of it, for sizing:
- Serve the canvas folder (or reuse the
sp-canvas dev server; Vite exposes the folder via /@fs/ because fs.allow includes it).
- Request one capture ID per board and run the Playwright injection for each; captures against one file can run in parallel. A canvas has 1 to 29 boards; the repo holds about 180. The
bulk-capture skill (CC0, see below) documents exactly this fan-out and the URL-hash format the capture script listens for.
- One
use_figma script per canvas that reads layout.json and arranges the captured frames into Figma sections per row, with the row title, board labels, numbering and link chips. Positions are the same ones the canvas computes in canvasLibrary.ts, so the Figma page would mirror the tldraw page.
- Optionally create a variable collection from
00-design-tokens.html first, so captures bind their colours to it.
Estimate: a few hundred lines, most of it the use_figma placement script, plus a sp-canvas figma <slug> or refkit subcommand to drive it. The ref-* boards should be excluded, since they are third-party captures.
Manual alternative with no code: a designer captures each board with the official Chrome extension or drags each .html into html.to.design, then arranges by hand. Fine for one canvas, tedious for the repo.
Source code availability
html.to.design
It is closed source, and since September 2025 the engine belongs to Figma.
- divRIOTS publishes no repository for html.to.design. Their GitHub organisation (
github.com/divriots) has 60-odd public repos (jampack, browser-vite, style-dictionary-to-figma, story-to-design, figma-to-website, create-figma-plugin) but none holds the converter. The story-to-design repo, which shares the engine, says in its README: "This repository is used for bug tracking purposes as the code is not open-source yet." The figma-to-website repo is likewise an issue tracker.
- There is no npm package;
html.to.design does not exist on the registry.
- On 2025-09-25 divRIOTS announced: "Figma has purchased the ‹div›RIOTS technology that turns webpages into Figma designs." The technology "is primarily used today in the plugins: html.to.design, code.to.design and story.to.design", and "html.to.design and all ‹div›RIOTS plugins will still be operated by us - independently". Figma uses it for Figma Make to canvas, the webpage capture in its Chrome extension, and the MCP
generate_figma_design capture.
- The one piece of the engine you can read is Figma's client-side capture script,
https://mcp.figma.com/mcp/html-to-design/capture.js: 386 KB, minified, references a source map, exposes window.figma.captureForDesign and a clipboard and multi-capture flow, and mentions h2d 17 times. It runs in the page, serialises the DOM, and posts to Figma, where the layer construction happens server-side. It is proprietary; reading it is fine, reusing it is not, and the server half is not visible at all. The plugin bundle Figma serves for html.to.design is in the same position.
Open-source projects that do the same job
| Project |
License |
Activity |
What it is |
Fit for our boards |
BuilderIO/figma-html |
MIT |
3.7k stars; lib/html-to-figma removed in the 2025-03-07 "project cleanup"; README now points to Builder's Chrome extension |
The original open-source DOM-to-Figma converter. Source still readable at commit 7fece25 under lib/html-to-figma/ (index.ts, helpers for frames, text, styles, svg, image, dimensions). Compiled JS also ships in npm @builder.io/html-to-figma@0.0.3 (2022). |
Best reference implementation to read. Old, unmaintained, but the DOM walk plus getComputedStyle plus getBoundingClientRect approach is the whole idea. |
sergcen/html-to-figma (npm html-figma) |
none stated |
107 stars, last push 2022 |
Split browser half (htmlTofigma(element) returns layer JSON) and plugin half (addLayersToFrame). Inspired by figma-html. |
Cleanest small codebase of the idea, but no license and abandoned. |
Lynavo/web2ui |
AGPL-3.0 |
Active, last push 2026-09-06, CI, releases |
Local-first Chrome extension: captures viewport or full document, prepares SVG/HTML clipboard data to paste into Figma. Has a commercial edition. |
Closest open-source analogue of the html.to.design extension. AGPL matters if it were ever bundled into this plugin. |
Yueyin-Tql/htmlToFigma |
MIT |
Jan 2026 |
MCP server plus browser extension; imports URL, HTML+CSS, or .h2d files; flex to auto layout; SVG to vectors; font mapping. README says it targets html.to.design feature parity. |
Interesting because it speaks .h2d and is MCP-shaped like our tooling. Small, one author. |
kevicebryan/open-htmltofigma |
MIT |
Jul 2026 |
Figma dev plugin: upload or paste a local HTML file, pick a viewport, get nested frames and text. |
Exactly our input shape (a local HTML file). Small. |
wangwang762/html-to-figma |
none stated |
Mar 2026 |
Plugin plus injected figma-capture.js plus a Python MCP server that opens the page; flex to auto layout, Google Fonts, gradients, shadows. |
Architecturally the same as Figma's MCP capture, in about five files. No license. |
mike2151/html-to-figma |
MIT |
May 2025 |
Plugin advertised as "open source version of html.to.design", motivated by LLM-generated HTML. |
Early, small. |
None of these is a drop-in for html.to.design's quality, and none is needed: the engine itself is now reachable for free through Figma's MCP and Chrome extension. They are worth reading if the canvas ever needs its own converter, for example to emit Figma nodes directly from gen.py measurements instead of through a rendered DOM. In that case start from BuilderIO/figma-html at commit 7fece25 for the conversion logic, and from wangwang762/html-to-figma or Yueyin-Tql/htmlToFigma for the capture-script and MCP plumbing.
Figma's official MCP
The server itself is not open source. https://mcp.figma.com/mcp is a hosted streamable-HTTP service; the Claude Code plugin's .mcp.json only points at it with a bundle-version header. The desktop variant (127.0.0.1:3845) is compiled into the Figma desktop app. The plugin's server.json names github.com/figma/mcp-server-guide as its "repository", but that repo holds documentation and skills, not server code, and carries no license file; the README says use is governed by the Figma Developer Terms.
What Figma does publish, and what each is good for here:
| Repo / artifact |
License |
What it is |
Use for us |
figma/mcp-server-guide (2.0k stars, pushed today) |
none stated |
The skills the Claude Code plugin ships: figma-use, figma-generate-design, figma-create-new-file, etc. Includes eight example Plugin API scripts under skills/figma-generate-library/scripts/ and five Python helpers under workflow-skills/video-interaction-mapper/. The local plugin cache is a byte-for-byte copy. |
The figma-generate-design skill documents the capture-then-use_figma workflow, including copying imageHash values out of a capture. The scripts are the closest thing to official use_figma reference code. |
figma/plugin-typings (MIT) |
MIT |
plugin-api-standalone.d.ts, 453 KB, the full Plugin API surface. The plugin vendors it at skills/figma-use/references/. |
The authoritative reference for any use_figma placement script. |
figma/code-connect (MIT, 1.6k stars) |
MIT |
TypeScript CLI that authenticates against Figma and reads and writes Code Connect data. |
Reference for auth and REST patterns only; it does not create design nodes. |
figma/rest-api-spec, figma/plugin-samples, figma/sds |
MIT |
OpenAPI spec for the REST API; sample plugins; a sample design system. |
Background. The REST API still cannot write design content. |
figma/community-resources (MIT) |
MIT |
Figma's curated list of open-source plugins, widgets and agent skills. |
One listed skill is directly relevant: bulk-capture (below). |
mcp.figma.com/mcp/html-to-design/capture.js |
proprietary |
The client half of the capture engine (see previous section). |
Readable, not reusable. |
Generative plugins (create_generative_plugin, update_generative_plugin) |
n/a |
Figma-hosted plugins whose code.ts and ui.html we author through the MCP. |
A source-controlled, persistent write path if the placement script ever outgrows one-off use_figma calls. Still runs on Figma's infrastructure. |
bulk-capture, the one piece worth copying
tallneil/tallneil-mono-public/.claude/skills/bulk-capture (CC0) is a Claude Code skill Figma lists under "Design Generation". It does exactly what a whole-canvas export needs: call generate_figma_design once per page to get N capture IDs, open N tabs at once through the Chrome DevTools MCP with the capture session in the URL hash, then poll all N IDs in parallel. It documents the hash format the capture script listens for:
<url>#figmacapture=<captureId>&figmaendpoint=https%3A%2F%2Fmcp.figma.com%2Fmcp%2Fcapture%2F<captureId>%2Fsubmit&figmadelay=2000
and the failure modes (redirects drop the hash; auth walls and short figmadelay give empty captures). The Playwright script in the proof of concept is the same flow driven from Python; the skill's version needs no injection because the boards would carry the <script> tag themselves, which for us means one line in gen.py behind a flag, or an injected tag in a served copy so committed boards stay clean.
Open-source MCP servers that write to Figma
These replace the hosted MCP with a local server plus a Figma plugin over WebSocket. All are MIT. None import HTML; they expose node-level tools (create frame, text, auto layout, image fill), so for our boards they would need one of the open-source HTML-to-Figma converters from the previous section in front of them.
| Project |
Stars |
Notes |
grab/cursor-talk-to-figma-mcp |
7.0k |
The original "talk to Figma" bridge, now maintained by Grab. set_image_fill accepts local files, URLs or base64. |
awdr74100/figwright |
703 |
112 tools, batch edits, multi-agent relay; active. |
gethopp/figma-mcp-bridge |
570 |
Plugin plus MCP server; positioned as bypassing API limits. |
alvinindra/figma-mcp-rust |
34 |
Rust port of vkhanhqui/figma-mcp-go; 73 read/write tools via plugin. |
Bottom line: there is no official server or engine source to reference. For a scripted whole-canvas export, reference bulk-capture for the orchestration and plugin-typings plus the figma-generate-library scripts for the placement step, and keep using the hosted generate_figma_design. A fully open stack (bridge server plus open-source converter) exists but trades the divRIOTS engine's quality for independence from Figma's service.
Recommendation
|
Official MCP capture |
Official Chrome extension |
html.to.design |
| Cost |
Free |
Free |
Free tier; PRO for bulk and unlimited |
| Input |
Live URL, script injected |
Live page in Chrome |
URL, extension .h2d, or the .html file itself |
| Scriptable |
Yes, from Claude Code |
No |
No (bulk import on PRO) |
| Verified here |
Yes, one board |
No |
No, but trusted from prior use |
| Engine |
divRIOTS html.to.design engine, owned by Figma |
Same engine |
Same engine, divRIOTS-operated plugin |
| Auto layout / styles |
Follows the source CSS; no styles created |
Same |
Same, plus Figma style creation |
| Variables |
Binds to existing variables |
Binds to existing variables |
Creates or applies styles |
- Per board, by hand: html.to.design's File tab (drag the board's
.html in) or the official Chrome extension. Both free, same engine underneath. html.to.design is the more complete UI (file and zip input, styles, bulk on PRO); the Figma extension needs no plugin install.
- Per board, scripted: the official MCP capture. It is the only route with an API, and it is already wired into this Claude Code setup.
- Per canvas: worth a small tool built on the MCP capture plus a
layout.json-driven placement script, if the demand is real. Skip until someone asks for a second canvas in Figma. html.to.design PRO bulk import is the no-code approximation: all boards in one go, arranged by hand.
- Not worth pursuing: tldraw export, REST API,
.fig generation, or paid third-party converters, since the free first-party route already produced the best result.
Sources
Research into whether a board (
mockups/canvases/<slug>/NN-*.html) or a whole canvas can be exported to Figma, which tools do it, and whether any of them have source code we can reference. Full report: embedded below (markshare link to follow once the account has a free slot).Findings
snapaction-ios/05-resource-detail.htmlpushed into Figma via the official Figma MCPgenerate_figma_designcapture (Playwright loads the served board, injects Figma'scapture.js, callscaptureForDesign). Result: 40 layers, 21 editable SF Pro text nodes, 0 missing fonts, images as image fills, bezel and corner radius intact. Absolute-positioned frames, which is faithful to the board's own CSS (.phone>*{position:absolute}).layout.json", i.e. a script. tldraw's SVG export does not help (thecanvas-fileshape has notoSvg, iframes cannot be flattened)..figwriter; the REST API is read-only for design content. The Plugin API is the only write path. Three wrappers around it: official MCP server, official Chrome extension, third-party plugins (html.to.design, html2design, Builder.io, others).mcp.figma.com/mcp/html-to-design/capture.jsand carriesh2dmarkers. The quality we have seen from html.to.design is what the scripted route produces.mcp.figma.com/mcpis a hosted service and the desktop variant is compiled into the app.figma/mcp-server-guideis docs and skills only, no license file.figma/plugin-typings(MIT, full Plugin API surface), the exampleuse_figmascripts infigma/mcp-server-guide, and the CC0bulk-captureskill listed infigma/community-resources, which does the parallel capture fan-out a canvas export needs. Open-source converters exist (BuilderIO/figma-htmlat commit7fece25,Yueyin-Tql/htmlToFigma,wangwang762/html-to-figma,Lynavo/web2uiAGPL) and open-source MCP bridges (grab/cursor-talk-to-figma-mcp,awdr74100/figwright), but none matches the divRIOTS engine's quality and none is needed while the hosted capture is free.Recommendation
.h2d, or the.htmlfile itself.htmlinto html.to.design's File tab, or use the official Chrome extension.generate_figma_design, already wired into this Claude Code setup.bulk-capturepattern), then oneuse_figmascript that readslayout.jsonand lays frames out in sections per row, mirroringcanvasLibrary.ts. Optionally create a variable collection from00-design-tokens.htmlfirst so captures bind colours. Excluderef-*boards. Estimate: a few hundred lines, mostly the placement script, plus ansp-canvas figma <slug>subcommand..figgeneration, paid converters.Proposed follow-up
sp-canvas figma <slug>on top of the MCP capture plus alayout.jsonplacement script.gen.py, or injection into a served copy so committed boards stay clean (the proof of concept used injection).No repo code was changed for this research.
Full report (research notes, ~4,000 words)
Date: 2026-09-08. Scope: can a single board (
mockups/canvases/<slug>/NN-*.html) or a whole canvas be turned into a Figma file, which tools exist for it, and whether any of those tools have source code we can reference. Research only; no repo code was changed.Short answer
snapaction-ios/05-resource-detail.html) was pushed into Figma through the official Figma MCP server's capture tool. Result: 40 layers, 21 editable SF Pro text nodes, zero missing fonts, images as image fills, phone bezel and corner radius intact. See "Proof of concept" below.layout.json", which is a script, not a product feature. A CC0 community skill,bulk-capture, already shows the parallel-capture half. tldraw's own SVG export does not help..figwriter and no HTML import in Figma itself. Every route goes through the Figma Plugin API, in one of three wrappers: the official MCP server, the official Chrome extension, or a third-party plugin/extension.figma/mcp-server-guide), the Plugin API typings (MIT), a CC0 bulk-capture skill, and a set of smaller open-source HTML-to-Figma converters and MCP bridges, listed under "Source code availability".generate_figma_design. It is free, already installed and authenticated in this Claude Code setup, scriptable, and it produced the best result. For a designer working by hand, the free official Chrome extension or html.to.design's File tab (drag the.htmlin) are the manual equivalents.What a mockup is, and why it matters for import
Each board is a single self-contained HTML file: inline CSS with custom properties as tokens, images inlined as
data:URIs, the system font stack (-apple-system/ SF Pro), plain flex plus absolute positioning, no web fonts, no SVG, no scripts. Sizes run from 6 KB to 1.8 MB per board (04-collection.htmlis the largest); a whole canvas is 80 KB to 7 MB of HTML.That is the easiest possible input for every HTML-to-Figma tool: no external resources to fetch, no auth, no JS state. The canvas renders boards in sandboxed
srcdociframes inside tldraw, so capturing the canvas page itself is not useful; the unit of export is the board file, served on its own URL or dropped in as a file.Ground truth about Figma's import surface
.fig, SVG, images, PDF (partial)..figfile written by a third partyfigma.createFrame,createText,createNodeFromSvg, image fills)foreignObjectis not imported.Routes, ranked for this repo
1. Official Figma MCP server,
generate_figma_design(verified)How it works: the MCP tool returns a capture ID and a capture script (
https://mcp.figma.com/mcp/html-to-design/capture.js). The script runs inside the page, serializes the rendered DOM (computed styles, text runs, images) and POSTs it to Figma, which materialises it as layers in a target file. For local pages Figma expects you to add a<script>tag and open the page with a#figmacapture=...hash; for external pages it documents Playwright injection. Playwright injection works for local pages too, which is what the proof of concept used, so the committed board HTML never has to change.Fit: excellent. Free with a Figma account (writing to non-draft files needs a Full seat, which the ReScience Lab Pro team has). Fully scriptable from Claude Code, one capture ID per board, and multiple capture IDs can run in parallel against one file. Limit: 50 MB per capture including serialized images, far above our largest board.
Caveats seen in the proof of concept:
use_figmaif you want instances. For a clone's screens, raw frames are the honest representation anyway..phoneabsolutely (.phone>*{position:absolute}) at measured coordinates, and the only flex container isbody. Boards built this way will never yield auto layout from any converter; text reflow after edits will not push siblings.transform: scale(1.02575)on the phone shell was baked into geometry: font size 17.23 instead of 16.8, phone width 441.07. Consistent with what the canvas inspector reports, but the numbers are not "clean".assets/art/05-shot.pngthere.00-design-tokens.htmlis the token board, anduse_figmacan create a variable collection from it.2. Official Figma Chrome extension (free, first party, manual)
Figma now ships a first-party Chrome extension that captures a webpage or a selected element into Figma Design or FigJam as editable layers, binding existing variables where available. Same engine and same output as route 1 ("plain Figma layers with variables bound if any are available", not mapped to components or styles). Privileged
chrome://pages cannot be captured; canvas-rendered or heavily animated sites may not translate cleanly.Fit: the manual counterpart of route 1. Serve a board folder (
python3 -m http.serverin the canvas folder, or the Vite dev server's/@fs/path), open a board, click capture. One board per click. No scripting, so it does not scale to 180 boards, but it is the zero-setup answer for "I want this one screen in Figma now".3. html.to.design by divRIOTS (plugin + Chrome extension, free tier)
The most established third-party tool. Three inputs: public URL, a Chrome extension that captures private or localhost pages and produces a
.h2dfile (or copies to the clipboard for direct paste), and a File tab that accepts.html,.htm,.zip,.mhtmland.h2ddirectly. Zip uploads are capped at 32 MB. The free plan includes local file upload, the extension, font mapping, auto layout and styles; PRO adds unlimited imports (1,000 per month fair use), bulk import of multiple files, and hi-res images.Fit: good, and the team already has positive hands-on experience with it (Yilin reports its output quality is good). Because our boards are single files with no external resources, the File tab path is the simplest manual route of all: drag
05-resource-detail.htmlinto the plugin. Bulk import (PRO) would take a whole canvas folder in one go, still without the row layout.Relation to the official capture: they are the same engine. Figma purchased divRIOTS's HTML-to-Figma technology in September 2025 (see "Source code availability" below). The MCP capture script is served from
mcp.figma.com/mcp/html-to-design/capture.jsand carriesh2dmarkers, html.to.design's own capture format. So the proof of concept above is, in effect, html.to.design's converter driven through Figma's API, and the quality you have seen from the plugin is what the scripted route produces. What the plugin still adds on top: its own UI for file, zip,.h2dand clipboard input, Figma style creation, and PRO bulk import. The auto layout difference I suspected earlier does not hold; the absolute frames came from the board's CSS.4. html2design (plugin, paste HTML, 10 free imports then $12/month)
Newer competitor. Paste raw HTML plus CSS into the plugin, or give a public URL; no extension. Claims editable native layers including SVG vectors. Ten free manual imports, then paid.
Fit: workable because a board is one pasteable file, but it offers nothing the free routes above lack.
5. Builder.io (Visual Copilot plugin + Chrome extension)
Builder's original open-source
figma-htmlimporter is retired in favour of its Chrome extension, which captures a page or a selected DOM element (including localhost and authenticated pages) and imports it through the Builder.io Figma plugin. Builder states 80 to 90 percent accuracy. The@builder.io/html-to-figmanpm package still exists but is no longer the supported path.Fit: usable, but the product is oriented toward Figma-to-code and its own platform; no reason to prefer it.
6. Other Chrome extensions and plugins seen
position: fixedsupport. Unverified.Routes that do not work
canvas-fileshape has notoSvg, and an iframe cannot be flattened into SVG anyway. At best each board becomes a raster, which Figma would import as a flat image. Only tldraw-native shapes (arrows, notes, text) would export as vectors, and the layouts do not persist any of those;layout.jsonholds only rows, files, titles, labels and links..figdirectly. No public format or writer.Proof of concept: what was actually done
mockups/canvases/snapaction-ios/withpython3 -m http.server 8765.generate_figma_designfor a capture ID, then ran a 15-line Playwright script (headless Chromium, 478 by 980 viewport, 2x scale) that loaded the board, injected Figma'scapture.js, and calledwindow.figma.captureForDesign(...).Measured on the result (read-only Plugin API query):
Screenshot of the Figma frame, rendered by Figma:
/tmp/figma-export-capture.png. It is visually indistinguishable from the board in the canvas.Delete the scratch file if it is not wanted; it lives in the team's drafts. The scratch server and script were stopped after the capture.
What a whole-canvas export would take
Not built, per instruction. The shape of it, for sizing:
sp-canvasdev server; Vite exposes the folder via/@fs/becausefs.allowincludes it).bulk-captureskill (CC0, see below) documents exactly this fan-out and the URL-hash format the capture script listens for.use_figmascript per canvas that readslayout.jsonand arranges the captured frames into Figma sections per row, with the row title, board labels, numbering and link chips. Positions are the same ones the canvas computes incanvasLibrary.ts, so the Figma page would mirror the tldraw page.00-design-tokens.htmlfirst, so captures bind their colours to it.Estimate: a few hundred lines, most of it the
use_figmaplacement script, plus asp-canvas figma <slug>orrefkitsubcommand to drive it. Theref-*boards should be excluded, since they are third-party captures.Manual alternative with no code: a designer captures each board with the official Chrome extension or drags each
.htmlinto html.to.design, then arranges by hand. Fine for one canvas, tedious for the repo.Source code availability
html.to.design
It is closed source, and since September 2025 the engine belongs to Figma.
github.com/divriots) has 60-odd public repos (jampack, browser-vite, style-dictionary-to-figma, story-to-design, figma-to-website, create-figma-plugin) but none holds the converter. Thestory-to-designrepo, which shares the engine, says in its README: "This repository is used for bug tracking purposes as the code is not open-source yet." Thefigma-to-websiterepo is likewise an issue tracker.html.to.designdoes not exist on the registry.generate_figma_designcapture.https://mcp.figma.com/mcp/html-to-design/capture.js: 386 KB, minified, references a source map, exposeswindow.figma.captureForDesignand a clipboard and multi-capture flow, and mentionsh2d17 times. It runs in the page, serialises the DOM, and posts to Figma, where the layer construction happens server-side. It is proprietary; reading it is fine, reusing it is not, and the server half is not visible at all. The plugin bundle Figma serves for html.to.design is in the same position.Open-source projects that do the same job
BuilderIO/figma-htmllib/html-to-figmaremoved in the 2025-03-07 "project cleanup"; README now points to Builder's Chrome extension7fece25underlib/html-to-figma/(index.ts, helpers for frames, text, styles, svg, image, dimensions). Compiled JS also ships in npm@builder.io/html-to-figma@0.0.3(2022).getComputedStyleplusgetBoundingClientRectapproach is the whole idea.sergcen/html-to-figma(npmhtml-figma)htmlTofigma(element)returns layer JSON) and plugin half (addLayersToFrame). Inspired by figma-html.Lynavo/web2uiYueyin-Tql/htmlToFigma.h2dfiles; flex to auto layout; SVG to vectors; font mapping. README says it targets html.to.design feature parity..h2dand is MCP-shaped like our tooling. Small, one author.kevicebryan/open-htmltofigmawangwang762/html-to-figmafigma-capture.jsplus a Python MCP server that opens the page; flex to auto layout, Google Fonts, gradients, shadows.mike2151/html-to-figmaNone of these is a drop-in for html.to.design's quality, and none is needed: the engine itself is now reachable for free through Figma's MCP and Chrome extension. They are worth reading if the canvas ever needs its own converter, for example to emit Figma nodes directly from
gen.pymeasurements instead of through a rendered DOM. In that case start fromBuilderIO/figma-htmlat commit7fece25for the conversion logic, and fromwangwang762/html-to-figmaorYueyin-Tql/htmlToFigmafor the capture-script and MCP plumbing.Figma's official MCP
The server itself is not open source.
https://mcp.figma.com/mcpis a hosted streamable-HTTP service; the Claude Code plugin's.mcp.jsononly points at it with a bundle-version header. The desktop variant (127.0.0.1:3845) is compiled into the Figma desktop app. The plugin'sserver.jsonnamesgithub.life-white.uk/figma/mcp-server-guideas its "repository", but that repo holds documentation and skills, not server code, and carries no license file; the README says use is governed by the Figma Developer Terms.What Figma does publish, and what each is good for here:
figma/mcp-server-guide(2.0k stars, pushed today)figma-use,figma-generate-design,figma-create-new-file, etc. Includes eight example Plugin API scripts underskills/figma-generate-library/scripts/and five Python helpers underworkflow-skills/video-interaction-mapper/. The local plugin cache is a byte-for-byte copy.figma-generate-designskill documents the capture-then-use_figmaworkflow, including copyingimageHashvalues out of a capture. The scripts are the closest thing to officialuse_figmareference code.figma/plugin-typings(MIT)plugin-api-standalone.d.ts, 453 KB, the full Plugin API surface. The plugin vendors it atskills/figma-use/references/.use_figmaplacement script.figma/code-connect(MIT, 1.6k stars)figma/rest-api-spec,figma/plugin-samples,figma/sdsfigma/community-resources(MIT)bulk-capture(below).mcp.figma.com/mcp/html-to-design/capture.jscreate_generative_plugin,update_generative_plugin)code.tsandui.htmlwe author through the MCP.use_figmacalls. Still runs on Figma's infrastructure.bulk-capture, the one piece worth copyingtallneil/tallneil-mono-public/.claude/skills/bulk-capture(CC0) is a Claude Code skill Figma lists under "Design Generation". It does exactly what a whole-canvas export needs: callgenerate_figma_designonce per page to get N capture IDs, open N tabs at once through the Chrome DevTools MCP with the capture session in the URL hash, then poll all N IDs in parallel. It documents the hash format the capture script listens for:and the failure modes (redirects drop the hash; auth walls and short
figmadelaygive empty captures). The Playwright script in the proof of concept is the same flow driven from Python; the skill's version needs no injection because the boards would carry the<script>tag themselves, which for us means one line ingen.pybehind a flag, or an injected tag in a served copy so committed boards stay clean.Open-source MCP servers that write to Figma
These replace the hosted MCP with a local server plus a Figma plugin over WebSocket. All are MIT. None import HTML; they expose node-level tools (create frame, text, auto layout, image fill), so for our boards they would need one of the open-source HTML-to-Figma converters from the previous section in front of them.
grab/cursor-talk-to-figma-mcpset_image_fillaccepts local files, URLs or base64.awdr74100/figwrightgethopp/figma-mcp-bridgealvinindra/figma-mcp-rustvkhanhqui/figma-mcp-go; 73 read/write tools via plugin.Bottom line: there is no official server or engine source to reference. For a scripted whole-canvas export, reference
bulk-capturefor the orchestration andplugin-typingsplus thefigma-generate-libraryscripts for the placement step, and keep using the hostedgenerate_figma_design. A fully open stack (bridge server plus open-source converter) exists but trades the divRIOTS engine's quality for independence from Figma's service.Recommendation
.h2d, or the.htmlfile itself.htmlin) or the official Chrome extension. Both free, same engine underneath. html.to.design is the more complete UI (file and zip input, styles, bulk on PRO); the Figma extension needs no plugin install.layout.json-driven placement script, if the demand is real. Skip until someone asks for a second canvas in Figma. html.to.design PRO bulk import is the no-code approximation: all boards in one go, arranged by hand..figgeneration, or paid third-party converters, since the free first-party route already produced the best result.Sources
toSvg: https://tldraw.dev/examples/toSvg-method-example