Utilities
@saastro/forms exports utility functions for component detection, missing component handling, and Tailwind CSS class generation. All are tree-shakeable — import only what you need.
Component Utilities
These functions help you determine which UI components a form needs and generate install commands for missing ones.
getRequiredComponents(config)
Analyzes a FormConfig and returns all component names needed to render it.
import { getRequiredComponents, FormBuilder } from '@saastro/forms';
const config = FormBuilder.create('contact')
.addField('name', (f) => f.type('text').label('Name'))
.addField('email', (f) => f.type('email').label('Email'))
.addField('date', (f) => f.type('date').label('Preferred Date'))
.addStep('main', ['name', 'email', 'date'])
.build();
const needed = getRequiredComponents(config);
// ['Button', 'Field', 'FieldLabel', 'FieldDescription', 'FieldError',
// 'Input', 'Label', 'FormField', 'FormControl',
// 'Calendar', 'Popover', 'PopoverTrigger', 'PopoverContent']
The function always includes core components (Button, Field, FieldLabel, FieldDescription, FieldError) plus any components required by the field types in the config. If a field uses tooltip, tooltip components are included automatically.
getMissingComponents(required, provided)
Compares required components against what’s in your registry.
import { getMissingComponents } from '@saastro/forms';
const missing = getMissingComponents(needed, {
Input,
Button,
Label,
Field,
FieldLabel,
FieldDescription,
FieldError,
FormField,
FormControl,
});
// ['Calendar', 'Popover', 'PopoverTrigger', 'PopoverContent']
getInstallCommand(missing)
Generates a npx shadcn@latest add command for missing components.
import { getInstallCommand } from '@saastro/forms';
const cmd = getInstallCommand(missing);
// "npx shadcn@latest add calendar popover"
Components are grouped by their shadcn package — Calendar maps to calendar, Popover/PopoverTrigger/PopoverContent all map to popover.
groupMissingByPackage(missing)
Groups missing component names by their shadcn package name:
import { groupMissingByPackage } from '@saastro/forms';
const grouped = groupMissingByPackage(missing);
// { calendar: ['Calendar'], popover: ['Popover', 'PopoverTrigger', 'PopoverContent'] }
fieldTypeComponents
The raw mapping of field type to required components:
import { fieldTypeComponents } from '@saastro/forms';
fieldTypeComponents['date'];
// ['Calendar', 'Popover', 'PopoverTrigger', 'PopoverContent', 'Button',
// 'FormField', 'FormControl', 'Field', 'FieldLabel', 'FieldDescription', 'FieldError']
fieldTypeComponents['text'];
// ['Input', 'Label', 'FormField', 'FormControl', 'Field', 'FieldLabel', 'FieldDescription', 'FieldError']
fieldTypeComponents['html'];
// [] — no components needed
coreComponents
Components that every form needs regardless of field types:
import { coreComponents } from '@saastro/forms';
// ['Button', 'Field', 'FieldLabel', 'FieldDescription', 'FieldError']
Tailwind Utilities
These functions generate Tailwind CSS classes for the form grid system. They’re used internally by useFormLayout but are exported for custom implementations.
getFormGridClass(layout?)
Generates the grid container classes and inline styles for the form.
import { getFormGridClass } from '@saastro/forms';
// Default (1 column, gap-4)
getFormGridClass();
// { className: 'grid grid-cols-1 gap-4', style: {} }
// Manual mode with 3 columns
getFormGridClass({ mode: 'manual', columns: 3, gap: 6 });
// { className: 'grid grid-cols-3 gap-6', style: {} }
// Auto mode (uses CSS grid auto-fit)
getFormGridClass({ mode: 'auto', minFieldWidth: 300, columns: 4, gap: 4 });
// { className: 'grid gap-4', style: { gridTemplateColumns: 'repeat(auto-fit, minmax(max(...), 1fr))' } }
In manual mode, it returns a grid-cols-{n} class. In auto mode, it uses inline gridTemplateColumns with auto-fit + minmax() because dynamic column counts can’t be detected by Tailwind at build time.
getFieldClass(formLayout?, fieldLayout?)
Generates responsive col-span-* and order-* classes for a field.
import { getFieldClass } from '@saastro/forms';
// Default
getFieldClass();
// 'col-span-1'
// Responsive columns
getFieldClass({ mode: 'manual', columns: 12 }, { columns: { default: 12, md: 6, lg: 4 } });
// 'col-span-12 md:col-span-6 lg:col-span-4'
// With order
getFieldClass({ mode: 'manual' }, { columns: { default: 6 }, order: { default: 2, lg: 1 } });
// 'col-span-6 order-2 lg:order-1'
// Auto mode — returns empty string (col-span not used with auto-fit)
getFieldClass({ mode: 'auto' }, { columns: { default: 6 } });
// ''
getHiddenClasses(hidden?)
Generates responsive visibility classes.
import { getHiddenClasses } from '@saastro/forms';
getHiddenClasses({ default: 'hidden', md: 'visible' });
// 'hidden md:block'
getHiddenClasses({ lg: 'hidden' });
// 'lg:hidden'
pxToRem(px)
Converts pixels to rem (base 16px). Used internally for auto-mode grid calculations.
import { pxToRem } from '@saastro/forms';
pxToRem(240); // 15
Test Data Generation
Lightweight utilities for generating realistic form data. No external dependencies (no faker.js). Supports locale detection and seeded randomness.
generateTestData(fields, options?)
Generates realistic test values for all fields in a form config. Auto-detects locale (Spanish/English) from field names and labels.
import { generateTestData, FormBuilder } from '@saastro/forms';
const config = FormBuilder.create('contact')
.addField('nombre', (f) => f.type('text').label('Nombre'))
.addField('email', (f) => f.type('email').label('Correo'))
.addField('telefono', (f) => f.type('tel').label('Telefono'))
.addField('plan', (f) =>
f
.type('select')
.label('Plan')
.options([
{ label: 'Basic', value: 'basic' },
{ label: 'Pro', value: 'pro' },
]),
)
.addStep('main', ['nombre', 'email', 'telefono', 'plan'])
.build();
const data = generateTestData(config.fields);
// {
// nombre: "Carlos Garcia", ← detected Spanish locale
// email: "usuario.test@example.com",
// telefono: "+34 612 345 678",
// plan: "pro" ← random option picked
// }
Options
interface TestDataOptions {
locale?: 'en' | 'es'; // Force locale (default: auto-detect)
seed?: number; // Seed for deterministic output
}
// Deterministic output (same seed = same values)
const data = generateTestData(config.fields, { seed: 42 });
generateFieldValue(name, config, options?)
Generates a realistic value for a single field. Uses a two-layer strategy:
- Label heuristics — Matches field name/label against known patterns (
nombre→"Carlos",email→"test.user@example.com") - Type fallback — If no pattern matches, generates appropriate data for the field type
import { generateFieldValue } from '@saastro/forms';
generateFieldValue('nombre', { type: 'text', label: 'Nombre' }, { locale: 'es' });
// "Carlos Garcia"
generateFieldValue('rating', { type: 'slider', min: 1, max: 5 }, { locale: 'en' });
// [3]
generateFieldValue('terms', { type: 'checkbox', label: 'Accept' }, { locale: 'en' });
// true
Skips non-input types (html, button, submit, next, back) by returning undefined.
detectLocale(fields)
Auto-detects whether form fields use Spanish or English based on field names and labels.
import { detectLocale } from '@saastro/forms';
detectLocale({
nombre: { type: 'text', label: 'Nombre completo' },
correo: { type: 'email', label: 'Correo electrónico' },
});
// 'es'
detectLocale({
name: { type: 'text', label: 'Full Name' },
email: { type: 'email', label: 'Email' },
});
// 'en'
Returns 'es' if 2+ Spanish patterns are found in field names/labels, otherwise 'en'.
Recognized Spanish patterns: nombre, apellido, correo, telefono, dirección, ciudad, provincia, código, mensaje, empresa, comentario, fecha, edad, género, contraseña, enviar, siguiente, atrás, acepto, términos
Field Transforms
applyFieldTransforms(fields, values)
Applies per-field transform properties to form values before submission. Returns a new object (does not mutate input).
This is called internally by useFormSubmit but exported for custom submit flows.
import { applyFieldTransforms } from '@saastro/forms';
const fields = {
email: { type: 'email', label: 'Email', transform: 'lowercase' },
name: { type: 'text', label: 'Name', transform: ['trim', 'uppercase'] },
notes: { type: 'textarea', label: 'Notes' }, // no transform
};
const values = {
email: ' User@Example.COM ',
name: ' john doe ',
notes: 'Hello world',
};
const result = applyFieldTransforms(fields, values);
// {
// email: " user@example.com ",
// name: "JOHN DOE",
// notes: "Hello world" ← unchanged
// }
Supports three transform formats:
- Single built-in:
'trim' - Chain:
['trim', 'lowercase']— applied left to right - Custom function:
(value) => String(value).trim()
See Field-Level Transforms for the full list of built-in transforms.
Related
- Component System — How component injection works
- Layout System — Grid configuration
- Submit & Actions — Field mapping and transforms
- Types Reference — Full type definitions