@letar/forms

Credit Card

Card number input with brand detection, expiry, and CVC

Full example

The complete sandbox example, read directly from the form-develop-app source at build time — switch framework/skin to see Form.Field.CreditCard wired to a real form. No shadcn/Vue/Angular equivalent exists yet — those tabs show as disabled.

apps/form-develop-app/src/app/credit-card-demo/page.tsx
'use client'

import { Box, Heading, Text, VStack } from '@chakra-ui/react'
import { Form } from '@letar/forms'
import { useState } from 'react'
import { DemoPageLayout } from '../_components'

export default function CreditCardDemoPage() {
  const [submittedData, setSubmittedData] = useState<Record<string, unknown> | null>(null)

  return (
    <DemoPageLayout
      title="Form.Field.CreditCard"
      description="Поле ввода данных банковской карты: номер с определением бренда, срок, CVC"
    >
      <VStack gap={8} align="stretch">
        <Box>
          <Heading size="md" mb={3}>
            Inline layout
          </Heading>
          <Text fontSize="sm" color="fg.muted" mb={4}>
            Номер карты, срок действия и CVC в одну строку. Иконка бренда определяется автоматически.
          </Text>
          <Form
            debug
            initialValue={{ card: { number: '', expiry: '', cvc: '' } }}
            onSubmit={(data) => setSubmittedData(data as Record<string, unknown>)}
          >
            <Form.Field.CreditCard name="card" label="Данные карты" layout="inline" />
            <Form.Button.Submit>Оплатить</Form.Button.Submit>
          </Form>
        </Box>

        <Box>
          <Heading size="md" mb={3}>
            Stacked layout
          </Heading>
          <Form
            debug
            initialValue={{ payment: { number: '', expiry: '', cvc: '' } }}
            onSubmit={(data) => setSubmittedData(data as Record<string, unknown>)}
          >
            <Form.Field.CreditCard name="payment" label="Карта оплаты" layout="stacked" />
            <Form.Button.Submit>Сохранить карту</Form.Button.Submit>
          </Form>
        </Box>

        {submittedData && (
          <Box p={4} bg="bg.subtle" borderRadius="md">
            <Heading size="sm" mb={2}>
              Отправленные данные:
            </Heading>
            <pre style={{ fontSize: '12px', overflow: 'auto' }}>{JSON.stringify(submittedData, null, 2)}</pre>
          </Box>
        )}
      </VStack>
    </DemoPageLayout>
  )
}

Overview

Form.Field.CreditCard is a compound input for payment card data: number (with auto-detection of Visa, MasterCard, Mir, etc.), expiry date, and CVC.

Basic Usage

<Form initialValue={{ card: { number: '', expiry: '', cvc: '' } }} onSubmit={handleSubmit}>
  <Form.Field.CreditCard name="card" label="Payment Card" />
  <Form.Button.Submit>Pay</Form.Button.Submit>
</Form>

Layouts

Inline — all fields in one row:

<Form.Field.CreditCard name="card" layout="inline" />

Stacked — fields stacked vertically:

<Form.Field.CreditCard name="card" layout="stacked" />

Props

PropTypeDefaultDescription
namestringField group name
labelstringLabel
brandsCardBrand[]allRestrict accepted brands
showBrandIconbooleantrueShow detected brand icon
layout'inline' | 'stacked''inline'Field layout
disabledbooleanfalseDisable input
readOnlybooleanfalseRead-only mode

Value

{ number: '4111111111111111', expiry: '12/28', cvc: '123' }

Brand Detection

Card brand is detected automatically from the first digits: Visa (4), MasterCard (51-55), Mir (2200-2204), American Express (34/37), and more.

Validation

Built-in Luhn algorithm validation for card numbers. Use creditCardSchema for Zod schema validation:

import { creditCardSchema } from '@letar/forms'

const schema = z.object({
  card: creditCardSchema,
})

Live Example

Try the interactive example on forms-example.letar.best.

On this page