Figma Font Substitution: How Missing Fonts Get Replaced and Logged

Why an imported page's fonts read as missing in Figma, how Figma and UnHTML each replace them, and how to read the import report before you ship.

REFERENCEDESIGNERPublished

Import a live webpage into Figma and there's a good chance at least one font comes in wrong. That's not a bug in the importer. It's two different font models meeting for the first time: the browser downloaded a font the moment the page loaded, but Figma only recognizes fonts that are already installed on your computer. Figma font substitution is what fills that gap. It matters because a swapped font can quietly change line length, wrapping, and vertical rhythm across an entire layout.

This piece walks through why the mismatch happens, how Figma flags and replaces a missing font on its own, what UnHTML substitutes by default when it converts a page, how to read the import report that comes out of every run, and where font licensing actually enters the picture once you're staring at someone else's typeface inside your own file.

Why a page's fonts go missing in the first place

A browser renders a page's fonts through the CSS @font-face rule, which lists one or more sources for a given font family and lets the browser pick the first usable one. The @font-face at-rule is explicit about ordering: a local() source should come before a url() source, so a font already installed on the visitor's machine gets used before anything is downloaded, and more specific format() or tech() hints should precede less specific ones. Font files referenced this way are same-origin by default, usable elsewhere only if the server sends CORS headers that permit it.

None of that requires the font to ever touch the visitor's font manager. A page can render its heading in a paid, carefully licensed display face and never once ask the browser to install it locally. The font-display descriptor even controls what a visitor sees while that download is still in flight, swapping in a system font temporarily and replacing it once the real file lands. From a reader's point of view, the whole exchange is invisible.

Figma works the opposite way. A text layer stores a font by family and style name and expects that exact font to be present in your local font manager. When it's not, Figma has nothing to fall back on by itself, so it just flags the layer. That single difference, download versus install, is the entire reason a page that looked correct in Chrome can hand an import tool a font it has never had access to.

How Figma flags a missing font on its own

Figma's own detection is straightforward once you know what to look for. A missing font shows a small icon in the left sidebar of the file. Select an affected text layer and the same flag surfaces next to the font name in the right sidebar. The usual causes: a font that simply isn't installed, a missing style within an otherwise-installed family, or collaborators running different versions of the same font.

From there, the Missing fonts modal lists every missing family and style in the file and lets you choose a replacement for each one from a dropdown. Clicking Replace fonts applies the swap across the whole file, for every collaborator, including anyone who does have access to the original font. Figma recommends saving a version first, since the replacement isn't scoped to just your own view.

Two limits are worth keeping in mind. Figma only supports .TTF and .OTF font files, and it doesn't support custom fonts at all on ChromeOS or Linux, where the font picker is limited to Google Fonts regardless of what's installed on the machine. If your team works across platforms, that alone can force a substitution a Mac or Windows user would never see.

What UnHTML substitutes, and how to change it

UnHTML's default behavior follows the same logic as Figma's own missing-font handling, just applied automatically at import time. Any font the importer doesn't have locally gets substituted with Inter, and the fallback family is configurable if Inter isn't the right default for a given project. Nothing about the swap happens silently. Every substitution is written into the report the import produces.

That report is worth reading on purpose, not skimming. For heavier layout questions, like which frames couldn't keep their structure and fell back to absolute positioning, the companion piece on what survives the conversion to layers covers that fallback path in more depth. Pricing and configuration details for adjusting the default fallback live on the pricing page.

Reading the import report

Every run produces a report covering three things: which fonts were substituted and what replaced them, which frames reverted to absolute position because their layout couldn't be represented in auto layout, and which images were skipped.

Read it before shipping a file, not after. A substituted heading font changes measured character width, and that can shift wrap points across a whole section in a way that's easy to miss at a glance. Two paragraphs that lined up neatly in the source page can end a line early or late once Inter, narrower than a lot of display faces, takes over. A frame that reverted to absolute position behaves differently the moment content length changes, since it no longer resizes the way an auto-layout frame would. Treat the report as a punch list, not a log you skim after the fact, and you catch both problems while they're still cheap to fix, before a client or teammate opens the file and starts asking why a heading wraps oddly.

Web font licensing, in plain terms

Font licensing is where the substitution decision stops being purely cosmetic. Web font formats exist for a licensing reason as much as a technical one: many type vendors who won't license a raw TrueType or OpenType file for web use are willing to license the same family packaged as WOFF or WOFF2, because the packaged format limits how the file can be reused. That's exactly why a font rendering cleanly on a page can't just be pulled down and installed as a desktop font without checking what the license actually allows.

In practice, a page's web font license almost never covers using the same file as an installed, desktop font inside a design tool, even for internal work. A web font license and a desktop license are commonly sold as separate line items from the same vendor, sometimes at different prices, and a font that's perfectly fine to serve from a server isn't automatically fine to install on a machine. The honest move, and the one a font substitution defaults to, is to swap in a fallback and log it, then decide deliberately whether to license or source the original family for the final file, the same way you'd check a stock photo's terms before reusing it in a new context. This isn't a pixel-exact reproduction of the source page's typography, and the import report exists precisely so that gap stays visible instead of getting papered over.

A missing font in a Figma import isn't really a failure. It's the predictable outcome of two font models, download and install, meeting for the first time. What matters is whether the substitution is visible: check the import report on every import, and treat a font swap as a decision to make deliberately, not a cosmetic detail to skim past.

Questions

Does UnHTML keep the original font if I own a license for it?
UnHTML only uses fonts installed locally on your machine. If the imported page's font is one you have installed under your own license, Figma will match it by name and UnHTML keeps it. If it is not installed, UnHTML swaps in Inter, or your configured fallback, and logs the swap in the import report rather than downloading or embedding the original file.
Why does Figma say a font is missing when the page clearly displayed it in the browser?
A browser renders a page's font-face fonts by downloading them from the site's own server, so no local copy is ever required. Figma layers work the opposite way: they reference a font by name and expect it to be installed on your computer. A page can display fine in a browser and still hand Figma a font it has never had access to, which is exactly when the missing font icon and Replace fonts workflow show up.
Can I just install the missing font myself instead of using the substitute?
Yes. If you hold a license that covers desktop use, for example a webfont license with a matching desktop use clause, or a separate license for the same family, install the TTF or OTF file locally, then use Figma's Missing fonts modal to replace the substitute with the real family. UnHTML's import report tells you exactly which fonts were swapped, so you know what to go looking for.
What does the import report actually list?
Three things, after every run: which fonts were substituted and what they were replaced with, which frames could not keep their auto layout structure and reverted to absolute position, and which images were skipped. It is meant to be read before you ship the file, not just glanced at, since a substituted heading font or a reverted frame can change the layout more than it looks like at first glance.