How to Import a Next.js App Running on Localhost Into Figma
A Next.js app on localhost imports into Figma like any other page: run the capture CLI against the local URL, hide the dev indicator, and wait for hydration.
What You Need Before You Start
A Next.js app running on localhost imports into Figma the same way a deployed one does. UnHTML's capture CLI takes a URL, and a local address is still a URL. Run the capture CLI against your dev server's URL and it behaves exactly as it would against a production domain. The two things that actually change are specific to Next.js's development mode, not to localhost itself.
Before you start, have these ready:
- Node installed, and the UnHTML capture CLI available on your machine.
- The Figma desktop app with the UnHTML plugin installed.
- Your Next.js app running and reachable at a known address, for example
http://localhost:3000. - The exact route you want to capture, not just the homepage. If the page needs data to load client-side, know what it looks like once that data has arrived.
With those in place, the import comes down to six steps: confirm the URL, pick a dev or production build, hide the dev-mode indicator, wait for hydration, run the capture, then read the import report.
Step-by-Step: Capturing a Next.js Dev Server
Step 1: Confirm the app is reachable at its local URL. Open the exact route in your browser first and check that it renders the way you expect. The capture CLI fetches whatever that URL returns at the moment you run it, so a half-started dev server or a route that 404s gets captured exactly as broken.
Step 2: Decide between a dev build and a production build. next dev is faster to iterate on, since you can tweak CSS and refresh. next build && next start strips out dev-only scaffolding before the page ever reaches the capture step, which gives a cleaner result. Dev mode is fine for a one-off import, once you handle Step 3. For a page you'll re-capture repeatedly while polishing layout, the production build saves you from redoing that step every time.
Step 3: Hide the Next.js dev-mode route indicator. In development, Next.js injects an on-screen indicator that shows context about the current route, positioned at bottom-left by default. UnHTML captures whatever is actually in the DOM, so that indicator comes in as a layer unless you remove it first. Set it in next.config.ts:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
devIndicators: false,
}
export default nextConfig
Want to keep the indicator for your own debugging instead? Move it out of the capture area with devIndicators: { position: 'top-right' }. Next.js's own devIndicators reference shows the position option shipping in Next.js 15.2.0, with the older appIsrStatus, buildActivity, and buildActivityPosition options it replaced removed outright in Next.js 16.0.0, so the config key may differ if you're on an older version. Either setting still lets Next.js surface real compile and runtime errors. You're only hiding the cosmetic badge.
Step 4: Let the page fully hydrate before you capture it. A dev server often loads local data more slowly than a production API, especially from a mock or a local database. If a client component is still showing a loading skeleton or spinner when the CLI fetches the page, that skeleton is what lands in Figma, not the finished layout. Watch the page settle in your browser before you move on.
Step 5: Run the capture CLI against the localhost URL.
node capture/cli/capture.js http://localhost:3000/your-route --out scene.json
This fetches the page with no CORS limits and does a full-page scroll, so content below the fold gets captured along with what's visible on load. The output is a scene.json file.
Step 6: Drag scene.json into the UnHTML plugin in Figma, then read the import report. The plugin renders the capture in a real browser context, measures every node, and builds native frames, text, images, vectors, and auto-layout from the page's own CSS. The report tells you what changed along the way. Any font not installed on your machine gets substituted, Inter by default, and every swap is logged. Any section too complex for a clean auto-layout mapping falls back to absolute positioning. This isn't a pixel-exact clone, and the report exists so you can see exactly where it diverged.
Why Localhost Is Just Another URL to the Capture CLI
The reason this works without special handling comes down to how the same-origin policy treats requests. A request only needs CORS headers when it crosses an origin boundary: a different protocol, domain, or port than the one it came from. A page fetching its own assets from its own origin never triggers that restriction, whether that origin is https://example.com or http://localhost:3000. The capture CLI fetches the page itself, so from its point of view a local dev server and a production domain are both just an origin it's requesting. Nothing more.
This is also why importing a React or Vue site into Figma and importing a Next.js app on localhost end up being the same workflow with the same tool. Next.js is a React framework, and the CLI doesn't care what framework rendered the HTML it fetched. What does differ with localhost specifically: there's no public DNS entry and no CDN sitting in front of it, so the CLI has to run on a machine that can actually reach your dev server's address. Run it on the same machine as the dev server, or on the same network, and that stops being an issue.
What the Capture CLI Can't See
The capture CLI makes an unauthenticated request to whatever URL you give it. If your route sits behind a local login, whether that's NextAuth, a middleware redirect, or your own session check, the CLI sees what an unauthenticated visitor sees: a login screen, or a redirect. Not your actual page.
The workaround is to capture a DOM that's already in the state you want, rather than asking the CLI to reproduce that state itself. Log in through your own browser, then use exporting self-contained HTML from that already-rendered, already-authenticated page, or save it as a Safari webarchive. Both are file-based inputs the UnHTML plugin accepts directly. No CLI fetch required.
The same limitation applies to any client-only data state. Capture a spinner, and a spinner is what you get. If your local app shows one while it waits on a slow local API, and you capture at that exact moment, that spinner becomes the layer in Figma, not the loaded content. Figma plugins build native scene nodes programmatically rather than placing a flattened image, so UnHTML's plugin builds real nodes from what the capture recorded, not from what you intended the page to eventually look like. Fidelity is bounded by the DOM's state at capture time, not by anything the plugin could infer afterward.
Conclusion
Localhost isn't a special case for UnHTML. The capture CLI treats a dev server address the same way it treats any other URL, because the same-origin rules that make CORS irrelevant to a page's own assets apply whether that page is served from localhost or a production domain. The adjustments worth making are specific to Next.js's development mode: hide the on-screen dev indicator before you capture, and wait for the page to finish hydrating so you're not importing a loading state. Once the import lands, turning the repeated sections into reusable components is the natural next step, covered in the piece on building Figma components from an import.
Frequently Asked Questions
Can UnHTML import a Next.js app that's only running on localhost?
Yes. The capture CLI takes any URL, including a localhost address, and fetches it with no CORS limits and a full-page scroll, writing a scene.json you drag into the Figma plugin. Nothing about the import path cares whether the URL resolves on the public internet or on 127.0.0.1. It just needs to be reachable from the machine running the CLI.
Do I need to deploy my Next.js app before importing it into Figma?
No. Deploying is one way to get a stable URL, but a running dev server (next dev) on localhost is just as capturable. The more important decision is dev build versus production build: next build && next start removes the on-screen dev indicator and any dev-only warnings from the DOM, giving a cleaner capture, but next dev works fine too once you hide the indicator.
Why does Next.js's dev-mode indicator show up as a layer in Figma?
UnHTML captures whatever is actually rendered in the page's DOM at the moment of capture, and the on-screen route indicator Next.js injects in development is part of that DOM. Set devIndicators: false in next.config.ts before capturing, or move it out of frame with the position option, and it won't appear in the import.
Does the capture CLI see content behind a login on my local app?
Not by default. The CLI fetches the URL fresh, so it only sees what an unauthenticated request renders. For a route that requires a session, log in in your own browser first and use a self-contained HTML export or a Safari webarchive of that already-authenticated page instead of pointing the CLI at the URL directly.
Should I capture a Next.js page in dev mode or build it for production first?
Production is cleaner when you can spare the build time: no dev indicator, no Fast Refresh scaffolding, and the same code paths your visitors see. Dev mode is faster to iterate on and is fine for most layout and spacing work, as long as you hide the indicator and wait for the page to fully hydrate before you capture it.
Questions
- Can UnHTML import a Next.js app that's only running on localhost?
- Yes. The capture CLI takes any URL, including a localhost address, and fetches it with no CORS limits and a full-page scroll, writing a scene.json you drag into the Figma plugin. Nothing about the import path cares whether the URL resolves on the public internet or on 127.0.0.1; it just needs to be reachable from the machine running the CLI.
- Do I need to deploy my Next.js app before importing it into Figma?
- No. Deploying is one way to get a stable URL, but a running dev server (next dev) on localhost is just as capturable. The more important decision is dev build versus production build: next build && next start removes the on-screen dev indicator and any dev-only warnings from the DOM, giving a cleaner capture, but next dev works fine too once you hide the indicator.
- Why does Next.js's dev-mode indicator show up as a layer in Figma?
- UnHTML captures whatever is actually rendered in the page's DOM at the moment of capture, and the on-screen route indicator Next.js injects in development is part of that DOM. Set devIndicators: false in next.config.ts before capturing, or move it out of frame with the position option, and it won't appear in the import.
- Does the capture CLI see content behind a login on my local app?
- Not by default. The CLI fetches the URL fresh, so it only sees what an unauthenticated request renders. For a route that requires a session, log in in your own browser first and use a self-contained HTML export or a Safari webarchive of that already-authenticated page instead of pointing the CLI at the URL directly.
- Should I capture a Next.js page in dev mode or build it for production first?
- Production is cleaner when you can spare the build time: no dev indicator, no Fast Refresh scaffolding, and the same code paths your visitors see. Dev mode is faster to iterate on and is fine for most layout and spacing work, as long as you hide the indicator and wait for the page to fully hydrate before you capture it.