Form primitive
Vue Font Picker Component
<DomFontPicker>Searchable Google Fonts picker that previews only visible families and keeps font network loading inside the mounted control.
Playground
Search and preview Google Fonts
Font picker playground
Open to browse around the selected font, then use the separate search field. Only the mounted selected label and visible rows request previews.
Properties
Control props
Selected font family or application-defined font value.
Lightweight family metadata. Font files are never bundled in this array.
Optionally restrict results to one Google Fonts category.
Number of metadata rows appended near the end. Preview fonts load only near the viewport.
Optional shared sample. Empty uses each family name as its own preview.
Google Fonts CSS v2 endpoint or an application-controlled compatible proxy.
Preferred side before collision handling.
viewport keeps the list inside the browser; anchor keeps it attached while scrolling.
Field props
Optional ID override. By default parent forms derive the input ID from the field path using underscores.
Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy.
Visible field label.
Optional helper copy below the field.
Placeholder shown when the control is empty.
Validation errors for this field.
Validators
No validators yet.
Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas.
Render default field chrome, or hide chrome while keeping form state wiring.
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { reactive } from 'vue';
import { DomFontPicker } from '@getdom/studio';
const data = reactive({
"modelValue": "Lato",
"id": "",
"name": "",
"label": "Font family",
"description": "",
"placeholder": "Search Google Fonts",
"required": false,
"disabled": false,
"readOnly": false,
"invalid": false,
"errors": [],
"visible": true,
"validators": [],
"validateOnBlur": true,
"chrome": "field",
"fonts": [],
"category": "",
"pageSize": 24,
"previewText": "",
"loadFonts": true,
"stylesheetUrl": "https://fonts.googleapis.com/css2",
"clearable": true,
"placement": "bottom",
"floatingMode": "viewport"
});
</script>
<template>
<DomFontPicker
v-bind="data"
@update:modelValue="data.modelValue = $event"
/>
</template>Demo
Choose a font family
The selected value is an ordinary family name; the application decides how and where to use it.
Type a family such as Lato. Only visible previews are requested.
Selected family
Lato
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { DomFontPicker } from '@getdom/studio';
import { computed, ref } from 'vue';
const family = ref('Lato');
const sampleStyle = computed(getSampleStyle);
/**
* Build the demonstration font stack from the selected family.
*
* @returns {Record<string, string>} Inline style for the preview sentence.
*/
function getSampleStyle() {
const escapedFamily = family.value.replace(/[\\']/g, '\\$&');
return { fontFamily: `'${escapedFamily}', sans-serif` };
}
</script>
<template>
<div class="grid w-full max-w-lg gap-4">
<DomFontPicker
v-model="family"
label="Font family"
description="Type a family such as Lato. Only visible previews are requested."
placeholder="Search Google Fonts"
/>
<div class="rounded-2xl border border-border bg-secondary/40 p-5">
<p class="text-xs font-medium uppercase tracking-wide text-muted-fg">Selected family</p>
<p class="mt-2 text-2xl text-canvas-fg" :style="sampleStyle">{{ family || 'Choose a font' }}</p>
</div>
</div>
</template>
Loading model
Preview fonts stay local to the picker
No font bundle
The built-in snapshot contains 1,946 family names and categories (27 September 2026), but no font files. Importing the control does not start a Google Fonts request.
Incremental batches
Select appends pageSize rows near its end. Only intersecting preview rows, a small overscan, and the selected label request CSS v2 fonts with exact text subsetting. Opening near a distant selection does not download the intervening fonts.
Explicit ownership
The stylesheet node is removed when the picker unmounts. Applications own persistent loading for content that uses the chosen font elsewhere.
Google API keys
The Google Fonts CSS API needs no key. The Developer API uses a key to retrieve catalogue metadata, not to make previews faster. For a fresh catalogue, fetch and cache it on your server (or at build time), keep the key there, and pass family/category records through fonts. Google’s catalogue endpoint does not supply this control’s cursor pagination; paginate your cached metadata if needed.
Reference
Props
Control props
| Name | Type | TS | Default | Description |
|---|---|---|---|---|
modelValue | string | string | '' | Selected font family or application-defined font value. |
fontsts | array | Array< | [] | Lightweight family metadata. Font files are never bundled in this array. |
category | '' | 'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace' | string | '' | Optionally restrict results to one Google Fonts category. |
pageSize | number | number | 24 | Number of metadata rows appended near the end. Preview fonts load only near the viewport. |
previewText | string | string | '' | Optional shared sample. Empty uses each family name as its own preview. |
loadFonts | boolean | boolean | true | Load the mounted selected label and visible previews from Google Fonts. |
stylesheetUrl | string | string | 'https://fonts.googleapis.com/css2' | Google Fonts CSS v2 endpoint or an application-controlled compatible proxy. |
clearable | boolean | boolean | true | Show a clear button after a font is selected. |
placement | 'bottom' | 'top' | 'right' | 'left' | string | 'bottom' | Preferred side before collision handling. |
floatingMode | 'viewport' | 'anchor' | string | 'viewport' | viewport keeps the list inside the browser; anchor keeps it attached while scrolling. |
Field props
| Name | Type | TS | Default | Description |
|---|---|---|---|---|
id | string | string | '' | Optional ID override. By default parent forms derive the input ID from the field path using underscores. |
name | string | string | '' | Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy. |
label | string | string | '' | Visible field label. |
description | string | string | '' | Optional helper copy below the field. |
placeholder | string | string | '' | Placeholder shown when the control is empty. |
required | boolean | boolean | false | Mark the field as required. |
disabled | boolean | boolean | false | Disable field interaction. |
readOnly | boolean | boolean | false | Show the value but prevent editing. |
invalid | boolean | boolean | false | Mark the field invalid. |
errorsts | array | object | string | Array< | [] | Validation errors for this field. |
visible | boolean | boolean | true | Show or hide the field. |
validators | array | Array<unknown> | [] | Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas. |
validateOnBlur | boolean | boolean | true | Run validators when the field loses focus. |
chrome | 'field' | 'none' | false | string | 'field' | Render default field chrome, or hide chrome while keeping form state wiring. |
Auto-generated from Font picker.props and inline _edit hints.
Events
| Name | Payload | Description |
|---|---|---|
| @update:modelValue | string | Fired with the selected font value. |
| @select | { item, value, label } | Fired when a font family is selected. |
| @query | string | Fired as the user searches the catalog. |
| @focus | — | — |
| @blur | — | — |
Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.