UnflowUI
Components

CardInput

A credit/debit card number field that auto-formats digits into groups and detects the card brand (Visa, Mastercard, Amex, and more) as the user types.

Overview

CardInput wraps Input and specializes it for credit/debit card numbers. It strips non-digit characters as you type, re-inserts spacing groups based on the detected card brand (via the credit-card-type library), and caps the length to that brand's valid digit count — so a Visa caps at 16 digits grouped as 4242 4242 4242 4242, while an Amex caps at 15 digits grouped as 3782 822463 10005.

The detected brand's logo renders automatically at the start of the field (falling back to a generic card icon when nothing matches yet), inputMode="numeric" is set so mobile devices show a numeric keypad, and a pattern restricts accepted characters to digits and spaces. type and startEl are fixed internally and can't be overridden.

Import

import { CardInput } from '@unflow.io/ui/components/CardInput';

Props

PropTypeDefaultDescription
namestringrequiredForm field name
labelstringVisible label above the field
valuestringControlled value — raw or formatted digits, re-formatted on render
onChangeChangeEventHandlerCalled with the re-formatted value after digits are stripped/grouped
placeholderstring'0000 0000 0000 0000'Placeholder text
hintstringHelper text below the label
maxLengthnumberauto (per detected brand)Overrides the auto-computed max length
disabledbooleanfalseDisables the input
requiredbooleanfalseMarks field as required
fullWidthbooleanfalseStretches to fill container
status'valid' | 'invalid' | 'error'Validation state — renders an InputStatus icon
statusMessagestringMessage shown in the InputStatus tooltip
sensitivebooleanfalseAdds a reveal/hide toggle over the value
sensitiveDefaultHiddenbooleantrueWhether value starts hidden
endIconReactNodeIcon inside the right edge
endElReactNodeElement attached to the right border (e.g. action button)
onClearMouseEventHandlerWires up a clear button — rendered when value is non-empty

All standard HTMLInputElement props are forwarded. type and startEl are not exposed — CardInput always renders as a text field with the detected brand icon in startEl.

Basic usage

const [value, setValue] = useState('');

<CardInput
  name="card-number"
  label="Card number"
  hint="Enter the 16-digit number on your card."
  value={value}
  onChange={(e) => setValue(e.currentTarget.value)}
  onClear={() => setValue('')}
/>

Brand detection

The leading digits determine which brand logo renders on the left. Try Visa (4…), Mastercard (51–55…), Amex (34/37…), and Discover (6011…) — an unrecognized or empty value falls back to a generic card icon.

<CardInput name="visa" label="Visa" value="4242424242424242" onChange={() => {}} />
<CardInput name="mastercard" label="Mastercard" value="5555555555554444" onChange={() => {}} />
<CardInput name="amex" label="American Express" value="378282246310005" onChange={() => {}} />
<CardInput name="discover" label="Discover" value="6011111111111117" onChange={() => {}} />

Status

<CardInput
  name="valid"
  label="Valid"
  status="valid"
  statusMessage="Looks good!"
  value="4242424242424242"
  onChange={() => {}}
/>
<CardInput
  name="invalid"
  label="Invalid"
  status="invalid"
  statusMessage="Check the card number."
  value="4242"
  onChange={() => {}}
/>
<CardInput
  name="error"
  label="Error"
  status="error"
  statusMessage="This card was declined."
  value="4000000000000002"
  onChange={() => {}}
/>

status renders an InputStatus icon at the top-right of the field. Hovering the icon shows statusMessage in a tooltip.

Disabled

<CardInput
  name="card-number-disabled"
  label="Card number"
  value="4242424242424242"
  disabled
  onChange={() => {}}
/>

Customization (slots & slotProps)

SlotTypeDescription
shortcutReactNodeCustom shortcut hint element
sensitiveHiddenIcon / sensitiveVisibleIconReactNodeIcon overrides for the reveal/hide toggle (when sensitive is set)
clearIconReactNodeIcon override for the clear button
Slot PropPropsDescription
startElHTMLDivElement propsWrapper around the auto-detected brand icon — only className/DOM attributes apply, the icon itself can't be replaced
endIcon / endElHTMLSpanElement / HTMLDivElement propsWrapper around endIcon / endEl
inputContainerHTMLDivElement propsThe input wrapper div
inputLabelOmit<IInputLabel, 'label' | 'hint' | 'required' | 'fullWidth'>The label component
clearButtonOmit<IIconButton, 'icon' | 'onClick'>The clear button
sensitiveButtonOmit<IIconButton, 'icon' | 'onClick'>The reveal/hide button
statusOmit<IInputStatus, 'status' | 'message'>The status icon

Accessibility

  • inputMode="numeric" and a digits/spaces-only pattern hint mobile browsers to show a numeric keypad without blocking paste of formatted numbers.
  • maxLength is capped to the detected brand's valid digit count (plus grouping spaces), preventing entry of an obviously-too-long number.
  • Label, required, and status/statusMessage behave exactly as in Input — the label is linked via htmlFor/id, and status is surfaced to screen readers through the InputStatus tooltip.