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.

Playground.vuevue
Install
npm install @getdom/studio
vue
<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

Install
npm install @getdom/studio
vue
<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

NameTypeTSDefaultDescription
modelValuestringstring''Selected font family or application-defined font value.
fonts
ts
[
	{
		family: "Lato",
		value: "Lato",
		category: "sans-serif",
	}
]
arrayArray<FontsItem
type FontsItem = {
	family?: string; // Family
	value?: string; // Value
	category?: string; // Fallback
};
>
[]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.
pageSizenumbernumber24Number of metadata rows appended near the end. Preview fonts load only near the viewport.
previewTextstringstring''Optional shared sample. Empty uses each family name as its own preview.
loadFontsbooleanbooleantrueLoad the mounted selected label and visible previews from Google Fonts.
stylesheetUrlstringstring'https://fonts.googleapis.com/css2'Google Fonts CSS v2 endpoint or an application-controlled compatible proxy.
clearablebooleanbooleantrueShow 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

NameTypeTSDefaultDescription
idstringstring''Optional ID override. By default parent forms derive the input ID from the field path using underscores.
namestringstring''Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy.
labelstringstring''Visible field label.
descriptionstringstring''Optional helper copy below the field.
placeholderstringstring''Placeholder shown when the control is empty.
requiredbooleanbooleanfalseMark the field as required.
disabledbooleanbooleanfalseDisable field interaction.
readOnlybooleanbooleanfalseShow the value but prevent editing.
invalidbooleanbooleanfalseMark the field invalid.
errors
ts
[
	{
		name: "Validation name",
		message: "Error message",
	}
]
array | object | stringArray<ErrorsItem
type ErrorsItem = {
	name?: string; // Name
	message?: string; // Message
};
>
[]Validation errors for this field.
visiblebooleanbooleantrueShow or hide the field.
validatorsarrayArray<unknown>[]Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas.
validateOnBlurbooleanbooleantrueRun validators when the field loses focus.
chrome'field' | 'none' | falsestring'field'Render default field chrome, or hide chrome while keeping form state wiring.

Auto-generated from Font picker.props and inline _edit hints.

Events

NamePayloadDescription
@update:modelValuestringFired with the selected font value.
@select{ item, value, label }Fired when a font family is selected.
@querystringFired 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.