Card Payment Input

A specialized input component for credit/debit card numbers. Features automatic card brand detection, number formatting with spaces, and Luhn validation. Supports all major card brands including Visa, Mastercard, American Express, Discover, Diners Club, JCB, and UnionPay.

Basic Usage

import { CardPaymentInput } from '@/components'

<CardPaymentInput placeholder="0000 0000 0000 0000" />

With Label and Hint

Add a label and helper text to guide users.

<CardPaymentInput
  label="Card number"
  hint="Enter your card number"
  required
/>

Enter your card number

Sizes

The input comes in three sizes: small (36px), medium (40px, default), and large (44px).

Small (36px)

Medium (40px) - Default

Large (44px)

Small

<CardPaymentInput size="sm" label="Card number" />

Medium (Default)

<CardPaymentInput size="md" label="Card number" />

Large

<CardPaymentInput size="lg" label="Card number" />

Card Brand Detection

The component automatically detects the card brand as the user types and displays the appropriate icon. It supports:

  • Visa - starts with 4
  • Mastercard - starts with 51-55 or 2221-2720
  • American Express - starts with 34 or 37
  • Discover - starts with 6011, 644-649, or 65
  • Diners Club - starts with 300-305, 36, or 38
  • JCB - starts with 3528-3589
  • UnionPay - starts with 62

Visa (starts with 4)

Mastercard (starts with 51-55)

American Express (starts with 34/37)

Discover (starts with 6011/65)

Diners Club (starts with 36)

JCB (starts with 35)

Card Number Formatting

The input automatically formats card numbers with spaces:

  • Standard cards (Visa, Mastercard, etc.): 0000 0000 0000 0000 (4-4-4-4)
  • American Express: 0000 000000 00000 (4-6-5)
  • Diners Club: 0000 000000 0000 (4-6-4)

Default (empty)

Filled (Visa)

Filled (Mastercard)

Filled (Amex)

Required Fields

Use the required prop to show a required indicator (blue asterisk).

<CardPaymentInput
  label="Card number"
  hint="This field is required"
  required
/>

This field is required

Optional Fields

Use showOptionalBadge to indicate optional fields.

<CardPaymentInput
  label="Card number"
  showOptionalBadge
/>

Error State

Show validation errors with the error prop. Pass a string to display an error message, or true for styling only.

Error with message

Please enter a valid card number

Error styling only

This card number is invalid

Error with Message

<CardPaymentInput
  label="Card number"
  error="Please enter a valid card number"
/>

Error Styling Only

<CardPaymentInput
  label="Card number"
  error
  hint="This card number is invalid"
/>

Disabled State

Disabled inputs cannot be interacted with.

<CardPaymentInput
  label="Card number"
  defaultValue="4111111111111111"
  disabled
/>

Icon Customization

Control the visibility of the left credit card icon and the right brand icon.

Without left icon

Without brand icon

Without any icons

Without Left Icon

<CardPaymentInput label="Card number" showLeftIcon={false} />

Without Brand Icon

<CardPaymentInput label="Card number" showBrandIcon={false} />

Controlled Input

Control the input value externally with real-time validation feedback. The onChange callback provides detailed card information including the raw value, formatted value, detected brand, and validation status.

const [value, setValue] = useState('')
const [cardInfo, setCardInfo] = useState<CardInfo | null>(null)

<CardPaymentInput
  label="Card number"
  value={value}
  onChange={(rawValue, info) => {
    setValue(rawValue)
    setCardInfo(info)
  }}
  error={cardInfo && !cardInfo.isPotentiallyValid}
  hint={cardInfo?.isValid ? 'Card number is valid!' : 'Enter a valid card number'}
  required
/>

// cardInfo contains:
// - rawValue: '4111111111111111'
// - formattedValue: '4111 1111 1111 1111'
// - cardBrand: 'visa'
// - isValid: true (passes Luhn check and correct length)
// - isPotentiallyValid: true (could become valid with more input)

Enter a valid card number

Raw value: (empty)
Formatted: (empty)
Brand: unknown
Valid: No

Validation

The component includes built-in Luhn algorithm validation. The onChange callback provides:

  • isValid - true when the card number passes the Luhn check AND has a valid length for the detected card type
  • isPotentiallyValid - true when the number could become valid with more input
<CardPaymentInput
  onChange={(value, cardInfo) => {
    if (cardInfo.isValid) {
      console.log('Valid card:', cardInfo.cardBrand)
    }
  }}
/>

All States Overview

Default States

Empty

Filled (Visa)

Error

Invalid card number

Disabled

Size Variants

Small

Medium

Large

Card Brands

Visa

Mastercard

Amex

Discover

Accessibility

  • Uses inputMode="numeric" for mobile number pad
  • Uses autoComplete="cc-number" for browser autofill
  • Labels are properly associated with inputs via htmlFor/id
  • aria-describedby connects hint/error text to the input
  • aria-invalid is set when in error state
  • aria-required is set for required fields
  • Focus states are clearly visible
  • Full keyboard navigation support

API Reference

CardPaymentInput Props

PropTypeDefaultDescription
labelstring-Label text above the input
hintstring-Helper text below the input
errorboolean | stringfalseError state or error message
size'sm' | 'md' | 'lg''md'Input size variant
valuestring-Controlled value (digits only)
defaultValuestring''Initial value for uncontrolled usage
onChange(value: string, cardInfo: CardInfo) => void-Callback when value changes
onCardBrandChange(brand: CardBrand) => void-Callback when card brand changes
showLeftIconbooleantrueShow credit card icon on left
showBrandIconbooleantrueShow detected brand icon on right
showOptionalBadgebooleanfalseShow "(Optional)" badge
showInfoIconbooleanfalseShow info icon in label
disabledbooleanfalseDisable the input
requiredbooleanfalseMark as required

All standard HTML input attributes (except type and onChange) are also supported.

CardInfo Type

The CardInfo object returned from onChange:

PropertyTypeDescription
rawValuestringCard number with digits only
formattedValuestringCard number with spaces
cardBrandCardBrandDetected card brand
isValidbooleanPasses Luhn check and valid length
isPotentiallyValidbooleanCould become valid with more input

CardBrand Type

type CardBrand =
  | 'visa'
  | 'mastercard'
  | 'amex'
  | 'discover'
  | 'diners'
  | 'jcb'
  | 'unionpay'
  | 'unknown'