Turn a local HTML file into editable Figma layers that match what the browser renders.
Free and open source. No account, no subscription, no URL crawling.
Get it on Figma · Quick start · Using it · How it works · Demo video · ★ Star the repo
The browser does the layout. The plugin measures the rendered page and rebuilds it as nested Frames, rich-text layers, vectors and image fills, so you can keep designing instead of redrawing.
examples/kitchen-sink.html in Chrome (left) and after import (right): 185 editable layers.
Install it from the Figma Community, then run Open HTML to Figma from the Plugins menu in any design file.
Or build it from source. Needs Figma desktop and Node 18+.
git clone https://github.com/kevicebryan/open-htmltofigma.git
cd open-htmltofigma
npm install && npm run build- In Figma: Plugins → Development → Import plugin from manifest… and pick
manifest.json - Run Open HTML to Figma, pick a viewport, add your HTML, then Import to Figma
| Step | What to do |
|---|---|
| Viewport | Desktop (1440 × 1024), Laptop (1280 × 832), Tablet (768 × 1024), Mobile (390 × 844), Slide (1920 × 1080), A4 (794 × 1123) or Letter (816 × 1056), or type any size. Media queries and vh units resolve against it. |
| File | Choose an .html file. If it uses local CSS, images or fonts, select them together with the page, or use or pick its folder, so relative href / src / url() references resolve. |
| Or paste | Paste HTML straight into the text box. A code block copied from the chat, fence and all, works too. React components can't be imported; the plugin says so and points you to Copy AI prompt. |
| Copy AI prompt | Copies a request to add to your AI chat, so it answers with one self-contained HTML file laid out for the size you picked. |
| Create as component | The imported root becomes a Component instead of a Frame. |
| Execute page scripts | For JS-rendered pages (Tailwind Play CDN, content built by a <script>). Entrance and scroll-reveal animations are settled before measuring. Off by default; only enable it for HTML you trust. |
The plugin remembers your viewport and Create as component between runs. Execute page scripts always starts off.
The import lands in the middle of your viewport, selected and zoomed to fit. It is named HTML Import · 1440px (or HTML Component · …). Its 40 most-used colors become local color styles under HTML/, linked to every layer that uses them, so editing one style recolors the whole import.
Closer match: import at the viewport you design at, and install the page's fonts in Figma. Fonts Figma doesn't have are substituted and listed after the import.
| HTML | Figma |
|---|---|
Flex / grid / absolute layout, vh units |
Nested Frames at the measured positions |
Paragraphs with <b>, <i>, links, inline <code> |
One editable rich-text layer each, same line breaks as the browser |
Solid, linear, radial and background-clip: text gradients |
Native fills |
Per-side borders, per-corner radii, shadows, rings, blur(), backdrop-filter |
Strokes, corner radii and effects |
Inline SVG, <use> sprites, <img src="*.svg"> |
Vector layers |
Images, background-image, <canvas> charts |
Image fills cropped like object-fit / background-size |
::before / ::after, list markers, form controls, rotate() |
Matching layers |
Try the pages in examples/: kitchen-sink.html covers most features, and tailwind-cdn.html needs Execute page scripts.
HTML → isolated browser render at your viewport → measure the DOM → Frames / Text / vectors / images in Figma
- The plugin UI (
ui.html) renders your page in a sandboxed iframe sized like a real window, waits for fonts and images, and settles animations. - It walks the DOM and records each box's geometry, paints, borders, radii, shadows and text runs, including where the browser broke lines.
- The main thread (
code.ts) resolves fonts and rebuilds the tree as Figma nodes with the same positions.
Positions are absolute, straight from the browser's layout, instead of being rewritten as Auto Layout. That's intentional: it keeps the import visually identical. More detail in ARCHITECTURE.md.
- Everything runs inside Figma: there is no backend, account, analytics or tracking, and the plugin stores nothing.
- The only network requests are the ones your page makes while it renders (its stylesheets, scripts, fonts and images). Nothing from your Figma file is sent anywhere.
- The plugin UI itself makes no requests; its typeface is bundled.
manifest.jsonallows every domain only because pages can reference any CDN.
Missing fonts are substituted, and Figma's copy of a web font can be slightly wider or narrower (optical sizes); line breaks are kept either way. Skew, 3D transforms, clip-path and masks keep the measured box, not the effect. Conic / repeating gradients and HTML inside SVG become images. Cross-origin iframes are placeholders.
| Path | What it is |
|---|---|
ui.html |
Plugin UI: file handling, the isolated render, DOM capture and asset rasterizing |
code.ts |
Figma main thread: builds nodes from the captured tree. npm run build compiles it to code.js |
manifest.json |
Plugin entry points and network access |
test/check.html |
Runs the real capture on examples/kitchen-sink.html and asserts the tree |
docs/ |
Logo, icon and promo images; promo.html, demo.html and record.mjs regenerate them |
npm run watch # rebuild code.js on change
python3 -m http.server 8765 # then open http://localhost:8765/test/check.html
node docs/record.mjs # re-record docs/demo.mp4 (needs Chrome + ffmpeg, server running)After changing the capture, run test/check.html, then import the files in examples/ in Figma desktop.


