Skip to main content

Getting Started

You need to collect a card from your users without raw card numbers ever touching your servers. This guide walks you through the complete setup: installing the SDK, creating card elements, validating input, and tokenizing the result.

By the end you will have a working payment form that collects card data securely and returns a token your backend can use to charge the card.

Prerequisites

You need a Public Application API key with the token:create permission. Public keys are safe to use in frontend code because they can only create tokens, not read sensitive data.

To create one, log in to the Basis Theory Portal, create a new application of type "Public", and grant the token:create permission.

Never use Management or Private API keys in frontend code. They can read raw token data and must stay server-side.

1. Install

npm install --save @basis-theory/web-elements@beta

2. Initialize

The SDK is initialized synchronously — you get a ready-to-use instance immediately. Elements are loaded lazily when they are first mounted.

import BasisTheory from '@basis-theory/web-elements';

const bt = BasisTheory('<PUBLIC_API_KEY>');

3. Add Card Elements

v3 uses three separate elements — one for the card number, one for the expiration date, and one for the CVV. Each renders as a secure iframe inside the container you specify.

Add containers to your HTML:

<form id="payment-form">
<div id="card-number-element"></div>
<div id="expiry-element"></div>
<div id="cvv-element"></div>
<button type="submit" id="submit-btn" disabled>Pay</button>
</form>

Create and mount the elements:

const cardNumberEl = bt.createElement('cardNumber');
const expiryEl = bt.createElement('expiry');
const cvvEl = bt.createElement('cvv');

await Promise.all([
cardNumberEl.mount('#card-number-element'),
expiryEl.mount('#expiry-element'),
cvvEl.mount('#cvv-element'),
]);

4. Wait for the Element to Be Ready

Each element emits a ready event once its iframe has fully loaded and the element is interactive. If you need to drive the element programmatically (call focus(), clear(), etc.) after mounting, wait for ready first.

cardNumberEl.on('ready', () => {
// Safe to call cardNumberEl.focus(), cardNumberEl.clear(), etc.
});

For straightforward collect-and-tokenize forms you do not need to wait for ready before the user can type. The element becomes interactive as soon as it mounts. ready is primarily useful when you need to drive the element programmatically.

5. Handle Validation State

Each element emits a change event whenever the user types. Use it to track whether all fields are complete and valid before enabling the submit button.

The change event payload is in event.detail:

  • isValid — input passes all validation rules
  • isEmpty — input is empty
  • error — validation error object, or null if valid
const submitBtn = document.getElementById('submit-btn');

let cardNumberValid = false;
let expiryValid = false;
let cvvValid = false;

function updateSubmitButton() {
submitBtn.disabled = !(cardNumberValid && expiryValid && cvvValid);
}

cardNumberEl.on('change', (event) => {
cardNumberValid = event.detail.isValid;
updateSubmitButton();
});

expiryEl.on('change', (event) => {
expiryValid = event.detail.isValid;
updateSubmitButton();
});

cvvEl.on('change', (event) => {
cvvValid = event.detail.isValid;
updateSubmitButton();
});

The change event payload also includes cardBrand, last4, and bin on cardNumber elements. See Events for the full reference.

6. Tokenize on Submit

When the user submits the form, pass the element references directly to bt.tokens.create. The SDK sends the raw card data directly from the iframes to Basis Theory — your application code never sees the card number.

document.getElementById('payment-form').addEventListener('submit', async (e) => {
e.preventDefault();

try {
const token = await bt.tokens.create({
type: 'card',
data: {
number: cardNumberEl,
expiration_month: expiryEl,
expiration_year: expiryEl,
cvc: cvvEl,
},
});

// Send token.id to your backend to charge the card
console.log('Token created:', token.id);
} catch (error) {
console.error('Tokenization failed:', error);
}
});

token.id is a safe, non-sensitive identifier you send to your backend. Use it with the Basis Theory API or a proxy to charge the card through your payment processor.

Complete Example

<!DOCTYPE html>
<html>
<head>
<title>Payment Form</title>
<!-- Copy the CDN script tag from the Install section above -->
</head>
<body>
<form id="payment-form">
<div id="card-number-element"></div>
<div id="expiry-element"></div>
<div id="cvv-element"></div>
<button type="submit" id="submit-btn" disabled>Pay</button>
</form>

<script>
document.addEventListener('DOMContentLoaded', async () => {
try {
// BasisTheory is available as a global after the CDN script loads
const bt = BasisTheory('<PUBLIC_API_KEY>');

const cardNumberEl = bt.createElement('cardNumber');
const expiryEl = bt.createElement('expiry');
const cvvEl = bt.createElement('cvv');

await Promise.all([
cardNumberEl.mount('#card-number-element'),
expiryEl.mount('#expiry-element'),
cvvEl.mount('#cvv-element'),
]);

const submitBtn = document.getElementById('submit-btn');
let cardNumberValid = false;
let expiryValid = false;
let cvvValid = false;

function updateSubmitButton() {
submitBtn.disabled = !(cardNumberValid && expiryValid && cvvValid);
}

cardNumberEl.on('change', (event) => {
cardNumberValid = event.detail.isValid;
updateSubmitButton();
});

expiryEl.on('change', (event) => {
expiryValid = event.detail.isValid;
updateSubmitButton();
});

cvvEl.on('change', (event) => {
cvvValid = event.detail.isValid;
updateSubmitButton();
});

document.getElementById('payment-form').addEventListener('submit', async (e) => {
e.preventDefault();
submitBtn.disabled = true;

try {
const token = await bt.tokens.create({
type: 'card',
data: {
number: cardNumberEl,
expiration_month: expiryEl,
expiration_year: expiryEl,
cvc: cvvEl,
},
});

console.log('Token created:', token.id);
} catch (error) {
console.error('Tokenization failed:', error);
updateSubmitButton();
}
});
} catch (error) {
console.error('Failed to initialize Elements:', error);
}
});
</script>
</body>
</html>

Next Steps