String input constrained by a mask pattern or preset. Ideal for credit cards, expiry dates, ZIP codes, and formatted identifiers.
- First argument is required: a built-in
MASKSpreset, a raw pattern string, or a{ pattern, tokens }object - Label the field separately with
label(...)since the constructor takes the mask, not the label - The renderer adapts width and input mode to the mask profile (numeric vs alphanumeric)
Defaults, inheritance & field methods
- First argument is required: a
MASKSpreset, a custom pattern string, or a{ pattern, tokens }object - defaultValue is
'' - Masked values are stored by default (separators like
/,-, spaces are preserved) - Shared methods: see Base field builder
- String builder methods: see field.text()
Mask-specific methods:
| Method | Type | Description |
|---|---|---|
storeRaw() | () => this | Stores the unformatted raw payload instead of the masked value. |
storeMasked() | () => this | Explicitly keeps the formatted masked value, which is the default behavior. |
showPlaceholder(char?) | char?: string | Renders placeholder characters inside the current value. |
showMaskInPlaceholder(charOrText?) | charOrText?: string | Renders the mask as placeholder text while keeping the actual value empty. |
tokens(map) | map: Record<string, RegExp> | Adds or overrides token characters for advanced masks. |
validateComplete(message?) | message?: string | Requires the entire mask to be filled before submit. |
Adaptive mask runtime
The built-in masked renderers now adapt to the mask instead of treating every mask like the same numeric field.
- Numeric-only masks keep numeric-friendly keyboard / input mode hints
- Alphanumeric masks stop behaving like numeric-only inputs when the token map accepts letters
maxLengthfollows the visible mask length, including separators- Short masks stay compact while longer masks can claim the width they need, which makes side-by-side layouts easier without hand-tuned widths
If you want the mask behavior but your own shell, prefix badge, trigger row, or fully custom layout, keep the field as field.masked(...) in the schema and drive the UI through form.fieldController(name).
Recipes
Patterns that showcase mask-specific strengths.
Credit card with placeholder
| 1 | import { MASKS } from '@runilib/react-formbridge' |
| 2 | |
| 3 | const schema = { |
| 4 | cardNumber: field.masked(MASKS.CARD_16) |
| 5 | .label('Card number') |
| 6 | .required() |
| 7 | .showMaskInPlaceholder() |
| 8 | .validateComplete('Card number is incomplete.'), |
| 9 | } |
Expiry + CVV side by side
| 1 | import { MASKS } from '@runilib/react-formbridge' |
| 2 | |
| 3 | const schema = { |
| 4 | expiry: field.masked(MASKS.EXPIRY).label('Expiry').required(), |
| 5 | cvv: field.masked(MASKS.CVV).label('CVV').required(), |
| 6 | } |
Raw backup code (strip separators)
| 1 | const schema = { |
| 2 | backupCode: field.masked('9999-9999') |
| 3 | .label('Backup code') |
| 4 | .storeRaw() |
| 5 | .validateComplete(), |
| 6 | } |
Custom plate with uppercase token
| 1 | const schema = { |
| 2 | plate: field.masked('LL-999-LL') |
| 3 | .label('Plate') |
| 4 | .tokens({ L: /[A-Z]/ }) |
| 5 | .uppercase(), |
| 6 | } |
French IBAN
| 1 | import { MASKS } from '@runilib/react-formbridge' |
| 2 | |
| 3 | const schema = { |
| 4 | iban: field.masked(MASKS.IBAN_FR) |
| 5 | .label('IBAN') |
| 6 | .required() |
| 7 | .validateComplete('IBAN is incomplete.'), |
| 8 | } |
Pattern syntax
9= digita= letter*= any character- Any other character is treated as a visible separator
- Use
tokens({...})to add custom mask characters such asLfor uppercase-only letters
Built-in MASKS presets
Pass any of these presets as field.masked(MASKS.X).label('Label').
Cards
CARD_169999 9999 9999 9999
CARD_AMEX9999 999999 99999
CARD_199999 9999 9999 9999 999
Security / expiry
CVV999
CVV_AMEX9999
EXPIRY99/99
Date / time
DATE_DMY99/99/9999
DATE_MDY99/99/9999
DATE_ISO9999-99-99
TIME_HM99:99
TIME_HMS99:99:99
DATETIME99/99/9999 99:99
Bank / finance
IBAN_FRaa99 9999 9999 9999 9999 9999 999
IBAN_DEaa99 9999 9999 9999 9999 99
IBAN_GBaa99 aaaa 9999 9999 9999 99
IBANaa99 9999 9999 9999 9999 9999 9999 99
BANK_NZ99-9999-9999999-99
SIREN999 999 999
SIRET999 999 999 99999
Postal / identity
ZIP_FR99999
ZIP_US99999
ZIP_US_PLUS499999-9999
POSTCODE_UKaa9 9aa
SSN999-99-9999
NIR_FR9 99 99 99 999 999 99
Other
IP_ADDRESS999.999.999.999
DURATION99:99:99.999
NUMBER_FR9 999 999