Capabilities · API
Local Font Access API
Published Updated
window.queryLocalFonts() resolves with an array of FontData objects describing the fonts installed on the user’s device, after a local-fonts permission prompt. Each FontData exposes the PostScript, full, family, and style names and a blob() method that returns the raw SFNT bytes, which is what design tools need to shape text with a user’s own fonts rather than a web font.
Support is desktop Chromium only: Chrome 103 and the Edge and Opera releases built on it. Chrome on Android, Firefox, and Safari have no implementation in any version (BCD api.Window.queryLocalFonts), and the method is [SecureContext], so it is also absent on http:// origins other than localhost.
Syntax
Section titled “Syntax”window.queryLocalFonts()window.queryLocalFonts(options)
fontData.blob()queryLocalFonts() returns a Promise<sequence<FontData>> sorted in ascending order by postscriptName. blob() returns a Promise<Blob> whose type is application/octet-stream. The specification says the browser is not required to report every installed font, so the result is the set the user agent is willing to expose, filtered by the user’s choice when the browser shows a picker.
Parameters
Section titled “Parameters”queryLocalFonts() takes one optional options argument, a QueryOptions dictionary with a single member.
| Member | Type | Required | Description |
|---|---|---|---|
postscriptNames |
sequence<DOMString> |
No | Only fonts whose PostScript name is in this list are returned, for example ["Verdana-Bold", "Arial"]. An empty list returns nothing; an absent member returns every selectable font. Matching is exact, so "Verdana Bold" (with a space) matches nothing. |
Each returned FontData has four read-only USVString attributes and one method.
| Member | Type | Description |
|---|---|---|
postscriptName |
USVString |
The PostScript name, such as "Arial-Bold"; also the sort key of the result. |
fullName |
USVString |
Family plus subfamily, such as "Arial Bold". |
family |
USVString |
The family name as CSS font-family would use it, such as "Arial". |
style |
USVString |
The subfamily or style name, such as "Regular" or "Bold Italic". |
blob() |
Promise<Blob> |
The font file bytes (SFNT container: TrueType, OpenType, WOFF, or WOFF2). |
Names are returned as single strings in the US English or user-language localisation of the font’s name table; a page that needs another language has to parse the name table from blob() itself.
Exceptions
Section titled “Exceptions”queryLocalFonts() rejects with the following DOMException names, in the order the specification checks them.
| Exception | Condition |
|---|---|
SecurityError |
The document’s origin is opaque (for example a sandboxed iframe without allow-same-origin); or the document is not allowed to use the local-fonts policy-controlled feature (default allowlist 'self'); or the call has no transient user activation. |
NotAllowedError |
The user denied the local-fonts permission prompt. |
blob() defines no rejections. Chromium adds rejections the specification does not list: NotSupportedError with Not yet supported on this platform. where the enumeration backend is missing, SecurityError with Page needs to be visible. when the tab is hidden, DataError with Font data exceeds memory limit., and UnknownError for any other backend failure (see the observed callout for the denied-permission case, which Chromium also handles differently).
Examples
Section titled “Examples”Both examples run from a click handler, detect queryLocalFonts on window, and fall back to the fonts the page ships itself when the API is missing or the user declines.
Filling a font picker from the device, with a bundled fallback list
Section titled “Filling a font picker from the device, with a bundled fallback list”The fallback list is the set of web fonts the page already loads, so the picker still has entries in Firefox, Safari, and mobile Chrome. An empty result in Chrome is treated the same way because of the denied-permission behaviour described above.
const BUNDLED = ['Inter', 'Source Serif 4', 'JetBrains Mono'];
async function listFontFamilies() { if (!('queryLocalFonts' in window)) return BUNDLED;
try { const fonts = await window.queryLocalFonts(); if (fonts.length === 0) return BUNDLED; // denied in Chrome, or nothing selectable return [...new Set(fonts.map((f) => f.family))]; } catch (err) { if (err.name === 'SecurityError' || err.name === 'NotAllowedError') return BUNDLED; throw err; }}
document.querySelector('#pick-fonts').addEventListener('click', async () => { const select = document.querySelector('#font-family'); select.replaceChildren(...(await listFontFamilies()).map((name) => new Option(name)));});Deduplicating on family matters because every weight and style of a family arrives as its own FontData: "Arial", "Arial-Bold", "Arial-Italic", and "Arial-BoldItalicMT" are four entries sharing the family "Arial".
Checking for one specific font before rendering a document with it
Section titled “Checking for one specific font before rendering a document with it”postscriptNames narrows the prompt and the result to the fonts you care about. When the font is absent, the document is rendered with its embedded web font instead, and the user is told why the layout may differ.
async function hasLocalFont(postscriptName) { if (!('queryLocalFonts' in window)) return false; try { const matches = await window.queryLocalFonts({ postscriptNames: [postscriptName] }); return matches.length === 1; } catch { return false; // no gesture, policy block, or user declined }}
document.querySelector('#open-document').addEventListener('click', async () => { const useLocal = await hasLocalFont('Verdana-Bold'); document.body.style.fontFamily = useLocal ? '"Verdana"' : '"Verdana Web", sans-serif'; document.querySelector('#font-note').hidden = useLocal;});blob() is only needed when you must read glyph outlines or the name table yourself (a canvas text editor, a font inspector); CSS can reference an installed font by family name without it.
See also
Section titled “See also”- File System Access API, the other desktop-Chromium file-level capability with a per-call user gesture
- Font and image optimization: font-display, picture, and AVIF/WebP, for the web-font fallback path
- PWAs on desktop
- Local Font Access API: queryLocalFonts() method (wicg.github.io)
- Local Font Access API: local-fonts permission (wicg.github.io)
- Window: queryLocalFonts() browser compatibility (developer.mozilla.org)
Specifications
| Specification | Status |
|---|---|
| Local Font Access API: queryLocalFonts() method | WICG draft |
| Local Font Access API: FontData interface | WICG draft |