Marble UI
← Components

Masked Input

Preview: Phone

Text field that formats input against a pattern as you type. `onValueChange` gives both masked and raw values.

Import

import { MaskInput } from '@marbleui/ui'

Props

<MaskInput>

PropTypeDefaultDescription
mask *required'card' | 'us-phone' | 'intl-phone' | 'expiry' | 'date' | 'postal' | 'iban' | (string & {})—
descriptionReactNode—
errorMessagestring | ((validation: ValidationResult) => string)—
labelReactNode—
onValueChange(masked: string, raw: string) => void—
placeholderstring—
prefixReactNode—Decorations inside the field, before / after the text.
suffixReactNode—
variantFieldVariant—

Also accepts React Aria props (119).

Usage notes

Text field that formats as you type, for card numbers, phone numbers, expiry dates and IBANs. For quantities use NumberField; for one-time codes use OTPField.

<MaskInput label="Card number" mask="card" onValueChange={(masked, raw) => setCardNumber(raw)} />
<MaskInput label="Order reference" mask="aa-9999" />
  • Presets: card, us-phone, intl-phone, expiry, date, postal (9999 aa) and iban. Any other string is a custom pattern: 9 a digit, a a letter (uppercased), * either; other characters are inserted for you.
  • The value lives inside the component: there’s no value; defaultValue prefills it (masked on the way in). Read it through onValueChange(masked, raw); raw drops the separators.
  • The placeholder defaults to the pattern (0000 0000 …), and digit-only patterns set inputMode="numeric" for a number keypad on touch devices.
  • A half-filled value still passes isRequired; check completeness with validate.
Example source · show
import { useState } from 'react'
import { MaskInput } from './mask-input'

function Phone() {
  const [raw, setRaw] = useState('')
  return <div className="flex w-64 flex-col gap-2"><MaskInput label="Phone" mask="us-phone" onValueChange={(_, r) => setRaw(r)} /><span className="text-xs text-fg-muted">Raw: {raw || '—'}</span></div>
}
export const styles = {
  Phone,
  Presets: () => <div className="grid w-[30rem] grid-cols-2 gap-3"><MaskInput label="Card number" mask="card" /><MaskInput label="Expiry" mask="expiry" /><MaskInput label="Date" mask="date" /><MaskInput label="Postal code (NL)" mask="postal" /><MaskInput label="IBAN" mask="iban" className="col-span-2" /></div>,
}

Source: packages/ui/src/mask-input/mask-input.tsx

Catalog

Category
inputs
Priority
Extra
Primitive
custom
React Aria
TextField
Base UI
Input