Field Library
FIELD_REGISTRY describes field types (text, email, select…) with type-level defaults. The field library is one level up: a curated set of fully-configured semantic fields — Full name, Email, Phone, Consent, Country… — each shipping its label, placeholder, serializable validators, and translations. Drop one into a form and you get the config and its translations in a single step, instead of re-wiring “email + required + format” every time.
It lives in its own lean subpath so it never weighs on the root bundle:
import { FIELD_LIBRARY, getFieldLibraryByGroup } from '@saastro/forms/library';
import type { FieldLibraryEntry, FieldLibraryI18n } from '@saastro/forms/library';
What’s in the box
24 fields, grouped for discovery. Each one carries a serializable schema (compiled to Zod at runtime) and a Spanish (es) translation overlay on top of the English base config.
| Key | Type | Group | Validators |
|---|---|---|---|
fullName | text | personal | required, minLength 2 |
firstName | text | personal | required, minLength 1 |
lastName | text | personal | required, minLength 1 |
age | number | personal | required, min 0, max 120, integer |
address | text | personal | — |
country | select | personal | required (12 countries, translated) |
birthdate | date | personal | required |
postalCode | text | personal | maxLength 12 |
email | email | contact | required, format email |
phone | phone | contact | required |
message | textarea | contact | required, maxLength 1000 |
company | text | commerce | — |
website | url | commerce | format url |
serviceType | select | commerce | required (Design/Dev/Consulting/…) |
budgetRange | select | commerce | required (budget brackets) |
projectDetails | textarea | commerce | required, minLength 20 |
consent | checkbox | survey | required, mustBeTrue (terms) |
marketingConsent | checkbox | survey | required, mustBeTrue (marketing) |
eventAttendance | radio | survey | required (yes/no/maybe, translated) |
guestCount | number | survey | min 1, max 10, integer |
dietaryNotes | textarea | survey | — |
dsaRequestType | select | survey | required (GDPR rights, translated) |
requestDetails | textarea | survey | — |
identityDeclaration | checkbox | survey | required, mustBeTrue |
An entry’s shape
interface FieldLibraryEntry {
/** The complete field config — base (English) strings + serializable validators. */
fieldConfig: FieldConfig;
/** Display name in the builder palette. */
label: string;
/** Optional lucide icon name; defaults to the field type's icon. */
icon?: string;
/** Short description for the palette tooltip. */
description?: string;
/** Grouping: 'personal' | 'contact' | 'commerce' | 'survey'. */
group: 'personal' | 'contact' | 'commerce' | 'survey';
/** Translations keyed by locale. The base config is the default language. */
i18n?: Record<string, FieldLibraryI18n>;
}
The base config is written in English; i18n.es overlays the Spanish strings (the same shape as a LocaleOverlay field entry — label, placeholder, helperText, description, options). For example:
FIELD_LIBRARY.email = {
label: 'Email',
group: 'contact',
icon: 'Mail',
description: 'Email address with format validation',
fieldConfig: {
type: 'email',
label: 'Email',
placeholder: '[email protected]',
schema: { required: true, format: 'email' },
},
i18n: { es: { label: 'Correo electrónico', placeholder: '[email protected]' } },
};
Using a library field
In a config
Spread the entry’s fieldConfig under whatever name you want. Because schema is a serializable ValidationRules object, the validators come along automatically:
import { FIELD_LIBRARY } from '@saastro/forms/library';
import type { FormConfig } from '@saastro/forms';
const config: FormConfig = {
formId: 'contact',
fields: {
email: FIELD_LIBRARY.email.fieldConfig,
message: FIELD_LIBRARY.message.fieldConfig,
},
steps: { main: { title: 'Contact', fields: ['email', 'message'] } },
buttons: { submit: { type: 'submit', label: 'Send' } },
};
Bringing the translations along
A library field’s i18n overlay is keyed by locale; re-key it under the field name and merge it into the form’s i18n.translations so the field renders translated when config.locale is set (see Internationalization):
const entry = FIELD_LIBRARY.email;
const config: FormConfig = {
formId: 'contact',
fields: { email: entry.fieldConfig },
steps: { main: { title: 'Contact', fields: ['email'] } },
i18n: {
defaultLocale: 'en',
locales: ['en', 'es'],
translations: {
es: { fields: { email: entry.i18n!.es } },
},
},
locale: 'es', // renders "Correo electrónico"
};
Grouped, for a picker
getFieldLibraryByGroup() returns the entries bucketed by group — handy for rendering a categorized palette:
import { getFieldLibraryByGroup } from '@saastro/forms/library';
const byGroup = getFieldLibraryByGroup();
// { personal: [...], contact: [...], commerce: [...], survey: [...] }
In the visual builder
The form builder surfaces the library in two places:
- Library section of the field palette — drag
Email,Full name,Country… onto a step. The field lands fully configured, and its bundled translation is merged into the form’si18nautomatically (so it shows no “untranslated” ⚠ in that language). - Save to library — configure any field, then save it as a reusable entry of your own. Your fields live in the browser (
localStorage) and appear in the same Library section under My fields, draggable just like the built-ins.
See also
- Validation — the
ValidationRuleseach entry’sschemauses. - Internationalization — the overlay model the bundled translations plug into.
- FormBuilder — the fluent API for assembling forms.