Element Types
v3 provides four element types. Each renders as a sandboxed iframe and exposes the same core interface.
| Type | createElement key | Description |
|---|---|---|
| Card Number | 'cardNumber' | PAN input with Luhn validation and brand detection |
| Expiry | 'expiry' | MM/YY expiration date input |
| CVV | 'cvv' | Card verification code input |
| Text | 'text' | General-purpose single-line text input |
Shared Options
All elements accept these options in createElement and element.update():
| Option | Type | Default | Description |
|---|---|---|---|
placeholder | string | — | Placeholder text shown when the input is empty |
ariaLabel | string | Element-specific | ARIA label for screen readers |
disabled | boolean | false | Disables the input |
readOnly | boolean | false | Makes the input read-only |
Shared Methods
Every element exposes the same interface:
| Method | Signature | Description |
|---|---|---|
mount | (selector: string | HTMLElement) => Promise<void> | Attaches the iframe to the DOM; resolves when the element is interactive |
unmount | () => void | Removes the iframe from the DOM and clears cached values |
update | (options: Partial<ElementOptions>) => Promise<void> | Updates options on a mounted element |
focus | () => void | Focuses the input |
blur | () => void | Blurs the input |
clear | () => void | Clears the current value |
on | (event, listener) => () => void | Subscribes to an event; returns an unsubscribe function |
Properties:
| Property | Type | Description |
|---|---|---|
id | string | Unique element identifier (read-only) |
type | ElementType | Element type string (read-only) |
mounted | boolean | Whether the element is currently mounted (read-only) |
Events:
| Event | Fires when… |
|---|---|
ready | The iframe has loaded and the element is interactive |
change | The user types or the value changes |
focus | The input gains focus |
blur | The input loses focus |
error | An infrastructure or API error occurs |
See Events for full payload shapes.
cardNumber
Collects the card PAN. Validates format and Luhn checksum, and detects the card brand as the user types.
- Web Elements
- React Elements
const cardNumberEl = bt.createElement('cardNumber', {
placeholder: '4242 4242 4242 4242',
ariaLabel: 'Card number',
});
await cardNumberEl.mount('#card-number-container');
import { useRef } from 'react';
import { CardNumberElement } from '@basis-theory/react-elements';
const cardNumberRef = useRef(null);
<CardNumberElement
ref={cardNumberRef}
placeholder="4242 4242 4242 4242"
ariaLabel="Card number"
onChange={(event) => console.log(event.detail.isValid)}
/>
change event
The cardNumber element emits additional fields on its change event beyond the shared ChangeEventDetail:
| Field | Type | Description |
|---|---|---|
cardBrand | string | Detected brand (e.g. 'visa', 'mastercard', 'amex') |
last4 | string | null | Last 4 digits — safe to display |
bin | string | null | BIN (first 6–8 digits) |
cvvLengths | number[] | null | Expected CVV length(s) for detected brand |
potentialBrands | string[] | All brands that could match at the current input length |
matchStrength | number | Brand detection confidence (0–1) |
cardNumberEl.on('change', (event) => {
const { isValid, cardBrand, last4, bin } = event.detail;
console.log(`${cardBrand} ...${last4}`, isValid);
});
Co-brand detection
Some card numbers match more than one brand (for example, a card that could be Mastercard or Maestro). The SDK detects this automatically and exposes potentialBrands on the change event. When potentialBrands.length > 1, you can prompt the user to pick their preferred brand:
cardNumberEl.on('change', (event) => {
const { cardBrand, potentialBrands, matchStrength } = event.detail;
if (potentialBrands && potentialBrands.length > 1) {
// Show brand picker UI with potentialBrands options
showBrandPicker(potentialBrands);
} else {
hideBrandPicker();
// cardBrand is the detected brand (e.g. 'mastercard')
}
});
matchStrength (0–1) indicates how confident the detection is — useful for deciding when to show or hide UI. A value approaching 1 means only one brand matches.
React ref
CardNumberElementRef extends the base ref with convenience state:
| Property | Type | Description |
|---|---|---|
complete | boolean | Input is valid and complete |
empty | boolean | Input is empty |
valid | boolean | Input passes validation |
cardBrand | string | Detected card brand |
last4 | string | Last 4 digits |
bin | string | BIN |
expiry
Collects the card expiration date in MM/YY format. Validates that the date is not in the past.
- Web Elements
- React Elements
const expiryEl = bt.createElement('expiry', {
placeholder: 'MM/YY',
});
await expiryEl.mount('#expiry-container');
import { ExpiryElement } from '@basis-theory/react-elements';
<ExpiryElement
placeholder="MM/YY"
onChange={(event) => console.log(event.detail.isValid)}
/>
The expiry element accepts only the shared options. When used in tokenization, pass the same element reference for both expiration_month and expiration_year — Basis Theory resolves them server-side:
bt.tokens.create({
type: 'card',
data: {
number: cardNumberEl,
expiration_month: expiryEl,
expiration_year: expiryEl,
cvc: cvvEl,
},
});
cvv
Collects the card verification code (CVV/CVC). When a cardNumber element is present on the same page, the SDK automatically updates the CVV element's expected length to match the detected card brand (3 digits for Visa/Mastercard, 4 for Amex).
- Web Elements
- React Elements
const cvvEl = bt.createElement('cvv', {
placeholder: '•••',
showToggle: true, // renders a show/hide button inside the field
});
await cvvEl.mount('#cvv-container');
import { CVVElement } from '@basis-theory/react-elements';
<CVVElement
placeholder="•••"
onChange={(event) => console.log(event.detail.isValid)}
/>
CVV-specific options
| Option | Type | Default | Description |
|---|---|---|---|
showToggle | boolean | false | Renders a show/hide toggle inside the field. Web elements only. Set at creation — cannot be changed via update(). |
text
General-purpose single-line text input. Use it to collect any non-card sensitive value (SSN, account number, routing number, etc.) without it touching your servers.
- Web Elements
- React Elements
const ssnEl = bt.createElement('text', {
placeholder: '•••-••-••••',
mask: [/\d/, /\d/, /\d/, '-', /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
transform: [/-/g, ''], // strip dashes before tokenization
validation: /^\d{9}$/, // must be exactly 9 digits after transform
});
await ssnEl.mount('#ssn-container');
import { TextElement } from '@basis-theory/react-elements';
<TextElement
placeholder="•••-••-••••"
mask={[/\d/, /\d/, /\d/, '-', /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]}
transform={[/-/g, '']}
validation={/^\d{9}$/}
onChange={(event) => console.log(event.detail.isValid)}
/>
Text-specific options
| Option | Type | Mutable via update() | Description |
|---|---|---|---|
validation | RegExp | No | Pattern the value must match to be considered valid |
required | boolean | Yes | Whether the field is required |
maxLength | number | Yes | Maximum character length |
password | boolean | Yes | Renders as a password field (type="password") |
inputMode | string | Yes | Mobile keyboard hint ('numeric', 'tel', 'email', etc.) |
mask | (RegExp | string)[] | No | Character-by-character input mask. Each position is a RegExp that the typed character must match, or a literal string character inserted automatically |
transform | [RegExp, string] | No | Applied to the value before tokenization: value.replace(pattern, replacement). Does not affect what the user sees |
validation, mask, and transform define the data contract for the element and are immutable after creation. To change them, unmount and recreate the element.Mask example
// Phone: (555) 123-4567
const phoneEl = bt.createElement('text', {
placeholder: '(555) 123-4567',
mask: [
'(', /\d/, /\d/, /\d/, ')',
' ', /\d/, /\d/, /\d/,
'-', /\d/, /\d/, /\d/, /\d/,
],
transform: [/\D/g, ''], // strip non-digits → "5551234567" is tokenized
});
For visual customization across all elements (colors, typography, spacing, border radius), see Theming.