Most builders share the same fluent inheritance tree, so the quickest way to read the API is to start with the common base surface and then add the builder-specific methods.
Inherited from BaseFieldBuilder:
| Method | Type | Description |
|---|---|---|
defaultValue | (value) => this | Overrides the default value set in the constructor. |
required | (message?) => this | Marks the field as required. A validation error is shown if the field is left empty. |
optional | () => this | Marks the field as optional (removes the required constraint). Useful when extending a schema where the field was previously required. |
label | (text) => this | Overrides the label set in the constructor. |
placeholder | (text?) => this | Sets the placeholder text displayed inside the field when it is empty. |
hint | (text) => this | Adds a helper/hint text displayed below the field to guide the user. |
disabled | (value = true) => this | Disables (or re-enables) the field. A disabled field is rendered but not interactive. |
hidden | (value = true) => this | Hides (or shows) the field. A hidden field is not rendered at all. |
debounce | (ms) => this | Sets the debounce delay (in milliseconds) for value changes. Validation and side-effects are deferred until the user stops typing for the specified duration. |
validate | (fn) => this | Adds a custom validation function to the field. Multiple validators can be chained - they run in order and the first error message returned is displayed. |
validateAsync | (fn) => this | Alias for validate() when the intent is explicitly asynchronous. Useful for username availability checks, server-side uniqueness validation, promo code verification, and other async rules. |
transform | (fn) => this | Registers a transform function that is applied to the field value before validation and submission (e.g. trimming whitespace, normalizing case). |
render | (fn) => this | Provides a custom render function to completely override the default field rendering. Use this when the built-in field components don't meet your UI needs. |
Conditional logic helpers:
| Method | Type | Description |
|---|---|---|
visibleWhen | (fieldOrFn, value?) => this | Makes the field conditionally visible based on another field's value or a custom predicate function. Multiple visibleWhen* calls are combined with AND logic. |
visibleAndRequiredWhen | (fieldOrFn, value?) => this | Makes the field both visible and required when the condition is met. Shorthand for calling visibleWhen() and requiredWhen() with the same condition. |
visibleWhenNot | (field, value) => this | Makes the field visible when the referenced field does not equal the given value. |
visibleWhenTruthy | (field) => this | Makes the field visible when the referenced field has a truthy value (any value that is not false, 0, '', null, or undefined). |
visibleWhenFalsy | (field) => this | Makes the field visible when the referenced field has a falsy value (false, 0, '', null, or undefined). |
visibleWhenAny | (pairs) => this | Makes the field visible when any of the given field/value pairs match (OR logic). |
requiredWhen | (fieldOrFn, value?) => this | Makes the field conditionally required based on another field's value or a custom predicate function. |
requiredWhenAny | (pairs) => this | Makes the field required when any of the given field/value pairs match (OR logic). |
disabledWhen | (fieldOrFn, value?) => this | Conditionally disables the field based on another field's value or a custom predicate function. |
resetOnHide | () => this | When the field becomes hidden (via a visibleWhen* condition), resets its value back to the default value. |
keepOnHide | () => this | When the field becomes hidden, keeps its current value intact. The value is preserved and will be included in form submission. |
clearOnHide | () => this | When the field becomes hidden, clears its value entirely (sets it to an empty/null state). |
Inherited from StringFieldBuilder:
| Method | Type | Description |
|---|---|---|
min | (length, message?) => this | Minimum string length. |
max | (length, message?) => this | Maximum string length. |
pattern | (regex | regex[], message?) => this | Regex pattern or accepted regex alternatives. |
patterns | (regexes, message?) => this | Alias for pattern() when passing multiple accepted regex alternatives. |
format | (regex, message?) => this | Internal base format pattern used by built-in field presets like email/url/tel. |
trim | () => this | Trim value before validation / submit. |
lowercase | () => this | Lowercase the value on every keystroke. |
uppercase | () => this | Uppercase the value on every keystroke. |
nonEmpty | (message?) => this | Rejects empty and whitespace-only strings (stricter than required()). |
length | (exact, message?) => this | Requires an exact character count for fixed-size codes. |
between | (min, max, message?) => this | Shorthand for a combined min+max length check. |
oneOf | (values, message?) => this | Restricts accepted values to an allow-list. |
notOneOf | (values, message?) => this | Blocks values from a deny-list (reserved words, forbidden slugs, etc.). |
matches | (fieldName, message?) => this | Must match the value of another field (e.g. confirm password). Supports ref() paths. |
sameAs | (fieldName, message?) => this | Alias for matches() with more explicit semantics. |
Special cases:
| Builder | Extends base with | Notes |
|---|---|---|
field.select() / field.radio() | options(...), optionsFrom(...), searchable(...) | Picker-style builders |
field.phone() | defaultCountry(), storeE164(), country-aware phone helpers | Extends base directly |
field.file() | Upload-focused surface | Main exception - does not expose render(), transform(), or the conditional helpers from BaseFieldBuilder |
Behavior vs styling
FormBridge keeps behavior and styling on separate layers. Pick the layer by the scope of the change, not the shape of the API:
| Layer | Scope | Where it lives |
|---|---|---|
| Business rules & reusable field behavior | Travels with the field definition everywhere it's rendered | On the builder - required(), validate(), transform(), visibleWhen(), render(), etc. |
| Shared visual theme | Applies to every field / form / submit button at once and can react to live state, schema metadata, and platform | useFormBridge(schema, { globalDefaults(ctx) }) |
| One-off styling exceptions | Overrides for a single render, not worth a builder or theme entry | Props on the rendered <form.fields.*> component |
Per-render styling props exposed on every generated field component:
- Wrapper:
style(cross-platform),className(web only) - Slot maps:
styles(style slot map),classNames(web class slot map) - Slot props forwarding:
wrapperProps,labelProps,hintProps,errorProps,inputProps,textareaProps,selectProps,searchInputProps - Render-hook escape hatches:
renderLabel,renderHint,renderError,renderRequiredMark,renderPicker,renderOption,renderEmpty,renderLoading,renderFileIcon
Rule of thumb: if the same visual repeats across fields, move it to globalDefaults. If the rule describes what the field is (validation, normalization, visibility), it belongs on the builder. Local component props win over globalDefaults, so per-render overrides always take precedence.
Shared method examples
Mini examples for the shared base builder methods:
defaultValue('FR')field.select('Country').defaultValue('FR')required('Required') / optional()field.text('Middle name').required('Required').optional()label('Username') / placeholder('@alex') / hint('Shown publicly')field.text('Handle').label('Username').placeholder('@alex').hint('Shown publicly')disabled() / hidden() / debounce(500)field.text('Referral code').disabled().hidden(false).debounce(500)validate((value) => value ? null : 'Missing')field.text('Company').validate((value) => value.length >= 2 ? null : 'Use at least 2 characters')transform((value) => value.trim())field.text('Slug').transform((value) => value.trim().toLowerCase().replace(/\s+/g, '-'))render(fn)field.custom(0).label('Rating').render(({ value, onChange }) => <Stars value={value} onChange={onChange} />)visibleWhen('accountType', 'company')field.text('Company name').visibleWhen('accountType', 'company')visibleAndRequiredWhen('accountType', 'company')field.text('Company name').visibleAndRequiredWhen('accountType', 'company')visibleWhenNot('role', 'guest') / visibleWhenTruthy('hasVat') / visibleWhenFalsy('sameAsBilling')field.text('VAT number').visibleWhenTruthy('hasVat')visibleWhenAny([['role', 'admin'], ['role', 'manager']])field.text('Internal note').visibleWhenAny([['role', 'admin'], ['role', 'manager']])requiredWhen('needsInvoice') / requiredWhenAny([['country', 'FR'], ['country', 'DE']])field.text('Tax ID').requiredWhenAny([['country', 'FR'], ['country', 'DE']])disabledWhen('submitted')field.text('Coupon code').disabledWhen('submitted')resetOnHide() / keepOnHide() / clearOnHide()field.text('Other').visibleWhen('reason', 'other').resetOnHide()_build()const descriptor = field.text('Debug').required()._build()Mini examples for the shared string builder methods:
min(3) / max(20)field.text('Username').trim().min(3).max(20)pattern(/^[A-Z]{3}-\d{4}$/)field.text('Partner code').pattern(/^[A-Z]{3}-\d{4}$/)patterns([/^FR-/, /^DE-/], 'Use an EU code')field.text('Region code').patterns([/^FR-/, /^DE-/], 'Use an EU code')format(/^https:\/\/.+$/, 'Use HTTPS')field.url('Webhook URL').format(/^https:\/\/.+$/, 'Use HTTPS')trim() / lowercase() / uppercase()field.email('Email').trim().lowercase()` and `field.masked('LL-999-LL').label('Plate').uppercase()matches('password') / sameAs('password')field.password('Confirm password').sameAs('password', 'Passwords must match')