react-formbridge
Browse documentation
Core conceptsv2.0.0

Builder basics

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:

MethodTypeDescription
defaultValue(value) => thisOverrides the default value set in the constructor.
required(message?) => thisMarks the field as required. A validation error is shown if the field is left empty.
optional() => thisMarks the field as optional (removes the required constraint). Useful when extending a schema where the field was previously required.
label(text) => thisOverrides the label set in the constructor.
placeholder(text?) => thisSets the placeholder text displayed inside the field when it is empty.
hint(text) => thisAdds a helper/hint text displayed below the field to guide the user.
disabled(value = true) => thisDisables (or re-enables) the field. A disabled field is rendered but not interactive.
hidden(value = true) => thisHides (or shows) the field. A hidden field is not rendered at all.
debounce(ms) => thisSets 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) => thisAdds 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) => thisAlias 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) => thisRegisters a transform function that is applied to the field value before validation and submission (e.g. trimming whitespace, normalizing case).
render(fn) => thisProvides 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:

MethodTypeDescription
visibleWhen(fieldOrFn, value?) => thisMakes 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?) => thisMakes the field both visible and required when the condition is met. Shorthand for calling visibleWhen() and requiredWhen() with the same condition.
visibleWhenNot(field, value) => thisMakes the field visible when the referenced field does not equal the given value.
visibleWhenTruthy(field) => thisMakes the field visible when the referenced field has a truthy value (any value that is not false, 0, '', null, or undefined).
visibleWhenFalsy(field) => thisMakes the field visible when the referenced field has a falsy value (false, 0, '', null, or undefined).
visibleWhenAny(pairs) => thisMakes the field visible when any of the given field/value pairs match (OR logic).
requiredWhen(fieldOrFn, value?) => thisMakes the field conditionally required based on another field's value or a custom predicate function.
requiredWhenAny(pairs) => thisMakes the field required when any of the given field/value pairs match (OR logic).
disabledWhen(fieldOrFn, value?) => thisConditionally disables the field based on another field's value or a custom predicate function.
resetOnHide() => thisWhen the field becomes hidden (via a visibleWhen* condition), resets its value back to the default value.
keepOnHide() => thisWhen the field becomes hidden, keeps its current value intact. The value is preserved and will be included in form submission.
clearOnHide() => thisWhen the field becomes hidden, clears its value entirely (sets it to an empty/null state).

Inherited from StringFieldBuilder:

MethodTypeDescription
min(length, message?) => thisMinimum string length.
max(length, message?) => thisMaximum string length.
pattern(regex | regex[], message?) => thisRegex pattern or accepted regex alternatives.
patterns(regexes, message?) => thisAlias for pattern() when passing multiple accepted regex alternatives.
format(regex, message?) => thisInternal base format pattern used by built-in field presets like email/url/tel.
trim() => thisTrim value before validation / submit.
lowercase() => thisLowercase the value on every keystroke.
uppercase() => thisUppercase the value on every keystroke.
nonEmpty(message?) => thisRejects empty and whitespace-only strings (stricter than required()).
length(exact, message?) => thisRequires an exact character count for fixed-size codes.
between(min, max, message?) => thisShorthand for a combined min+max length check.
oneOf(values, message?) => thisRestricts accepted values to an allow-list.
notOneOf(values, message?) => thisBlocks values from a deny-list (reserved words, forbidden slugs, etc.).
matches(fieldName, message?) => thisMust match the value of another field (e.g. confirm password). Supports ref() paths.
sameAs(fieldName, message?) => thisAlias for matches() with more explicit semantics.

Special cases:

BuilderExtends base withNotes
field.select() / field.radio()options(...), optionsFrom(...), searchable(...)Picker-style builders
field.phone()defaultCountry(), storeE164(), country-aware phone helpersExtends base directly
field.file()Upload-focused surfaceMain exception - does not expose render(), transform(), or the conditional helpers from BaseFieldBuilder

Behavior vs UI

Builders describe form semantics: defaults, validation, normalization, labels, hints, conditions, and type-specific metadata. They do not style or generate inputs.

Read that metadata from fieldController(name) and pass it to your application components. Reusable visual rules belong in your design-system adapters; reusable form behavior belongs in the schema.

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')