Migration Guide: v2 → v3
This guide covers every breaking change between @basis-theory/web-elements v2 and v3. Work through each section and use the upgrade checklist at the end to confirm your integration is complete.
@basis-theory/web-elements. Only the major version changes.1. Initialization
The most significant change in v3: initialization is now synchronous. The basistheory named export is replaced by a default BasisTheory constructor that returns an SDK instance immediately.
- import { basistheory } from '@basis-theory/web-elements';
+ import BasisTheory from '@basis-theory/web-elements';
- const bt = await basistheory('<PUBLIC_API_KEY>', { environment: 'test' });
+ const bt = BasisTheory('<PUBLIC_API_KEY>');
The environment option no longer exists. The SDK auto-detects the environment from the page origin (localhost → dev, btsandbox.com → UAT, everything else → production). To point at a specific API, use apiBaseUrl:
// Explicit API base URL (optional)
const bt = BasisTheory('<PUBLIC_API_KEY>', {
apiBaseUrl: 'https://api.btsandbox.com',
});
CDN global also changed from lowercase to uppercase:
- const bt = await basistheory('<PUBLIC_API_KEY>');
+ const bt = BasisTheory('<PUBLIC_API_KEY>');
2. Element types
Several element types were renamed, and two were removed.
| v2 type | v3 type | Notes |
|---|---|---|
'cardNumber' | 'cardNumber' | Unchanged |
'cardExpirationDate' | 'expiry' | Renamed |
'cardVerificationCode' | 'cvv' | Renamed |
'text' | 'text' | Unchanged |
'card' | — | Removed. Use cardNumber + expiry + cvv separately |
'copyButton' | — | Removed |
The combined card element no longer exists. Replace it with three individual elements:
- const cardEl = bt.createElement('card', { targetId: 'card' });
- await cardEl.mount('#card');
+ const cardNumberEl = bt.createElement('cardNumber');
+ const expiryEl = bt.createElement('expiry');
+ const cvvEl = bt.createElement('cvv');
+
+ await Promise.all([
+ cardNumberEl.mount('#card-number'),
+ expiryEl.mount('#expiry'),
+ cvvEl.mount('#cvv'),
+ ]);
You will need to add two additional containers to your HTML:
- <div id="card"></div>
+ <div id="card-number"></div>
+ <div id="expiry"></div>
+ <div id="cvv"></div>
3. Events
Removed events
keydown, click, and copy events are not available in v3.
Added events
error is a new event in v3, fired when infrastructure or API errors occur on the element.
element.on('error', (event) => {
console.error(event.detail.code, event.detail.message);
});
Change event payload
The change event payload shape changed. Access event data via event.detail instead of directly on event:
- element.on('change', (event) => {
- if (event.complete) { ... }
- if (event.empty) { ... }
- if (event.errors) { ... }
- });
+ element.on('change', (event) => {
+ if (event.detail.isValid) { ... }
+ if (event.detail.isEmpty) { ... }
+ if (event.detail.error) { ... }
+ });
cardNumber change event — card metadata fields moved to event.detail:
- event.cardBrand
- event.cardLast4
- event.cardBin
+ event.detail.cardBrand
+ event.detail.last4
+ event.detail.bin
on() return value
on() now returns an unsubscribe function instead of a Subscription object:
- const subscription = element.on('change', handler);
- subscription.unsubscribe();
+ const unsubscribe = element.on('change', handler);
+ unsubscribe();
4. createElement options
The targetId option was removed. The target container is now passed to mount() instead:
- const el = bt.createElement('cardNumber', { targetId: 'my-card' });
- el.mount();
+ const el = bt.createElement('cardNumber');
+ await el.mount('#my-card');
Validation state tracking changed. Instead of reading state from element properties after a change event, read from event.detail:
- cardElement.on('change', () => {
- submitBtn.disabled = !cardElement.complete;
- });
+ cardNumberEl.on('change', (event) => {
+ submitBtn.disabled = !event.detail.isValid;
+ });
5. Tokenization
tokens.create is unchanged for card tokens. The data object field names are the same:
// Same in v2 and v3
await bt.tokens.create({
type: 'card',
data: {
number: cardNumberEl,
expiration_month: expiryEl,
expiration_year: expiryEl,
cvc: cvvEl,
},
});
The tokenize method (batch) signature is unchanged.
tokenIntents gains a get() method in v3 (was only create() in v2):
// New in v3
const intent = await bt.tokenIntents.get(intentId, { apiKey: sessionApiKey });
6. Theming
v3 introduces a design token system. Instead of passing CSS properties directly to elements for global styling, you now pass structured theme and optional darkTheme objects to BasisTheory():
- // v2: per-element style only, no global theme
- const el = bt.createElement('cardNumber', {
- style: { base: { color: '#111' } },
- });
+ // v3: design tokens at init — no per-element style option
+ const bt = BasisTheory('<PUBLIC_API_KEY>', {
+ themeMode: 'auto',
+ theme: {
+ colors: { primary: '#6366f1', text: { primary: '#111827' }, ... },
+ typography: { fontFamily: 'Inter', fontSize: { base: '16px' }, fontWeight: { normal: '400' } },
+ spacing: { sm: '8px', md: '12px', lg: '16px' },
+ borders: { radius: { base: '6px' }, width: { base: '1.5px' } },
+ },
+ });
+
+ const el = bt.createElement('cardNumber');
See Theming for the full token schema and dark mode setup.
7. React
Provider
The useBasisTheory(apiKey, options) initialization hook is replaced by BasisTheoryProvider:
- import { useBasisTheory, BasisTheoryProvider } from '@basis-theory/react-elements';
-
- function App() {
- const { bt, error } = useBasisTheory('<PUBLIC_API_KEY>', { environment: 'test' });
- if (!bt) return <div>Loading...</div>;
- return <BasisTheoryProvider bt={bt}><PaymentForm /></BasisTheoryProvider>;
- }
+ import { BasisTheoryProvider } from '@basis-theory/react-elements';
+
+ function App() {
+ return (
+ <BasisTheoryProvider apiKey='<PUBLIC_API_KEY>'>
+ <PaymentForm />
+ </BasisTheoryProvider>
+ );
+ }
useBasisTheory() inside the tree still works but now takes no arguments — it only reads from context:
- const { bt } = useBasisTheory('<PUBLIC_API_KEY>', options);
+ const { bt, error } = useBasisTheory(); // must be inside BasisTheoryProvider
Components
CardElement is removed. Replace it with the three separate element components:
- import { CardElement } from '@basis-theory/react-elements';
-
- <CardElement id="card" ref={cardRef} onChange={(e) => setComplete(e.complete)} />
+ import { CardNumberElement, ExpiryElement, CVVElement } from '@basis-theory/react-elements';
+
+ <CardNumberElement ref={cardNumberRef} onChange={(e) => setCardNumberValid(e.detail.isValid)} />
+ <ExpiryElement ref={expiryRef} onChange={(e) => setExpiryValid(e.detail.isValid)} />
+ <CVVElement ref={cvvRef} onChange={(e) => setCvvValid(e.detail.isValid)} />
Event prop payloads follow the same change as the web SDK — use event.detail.*:
- onChange={(event) => setComplete(event.complete)}
+ onChange={(event) => setValid(event.detail.isValid)}
Upgrade checklist
Not yet available in V3
Services
bt.client— direct HTTP client for third-party endpoint requests
Element options
binLookup— BIN metadata lookup (brand, funding, issuer) returned in the change eventbinInfoobject- Per-element
styleoverrides — state variants (base,error,empty,complete) and pseudo-selectors (:hover,:focus,:disabled,::placeholder,::selection) - Custom Google Fonts — loading from the Google Fonts library
Methods
setValueRef(element)— synchronize one element's value with another (e.g., read-only display mirroring)validate()— programmatically trigger validationmonth()/year()/format()— expiry date helper methodsgetElement(id)— retrieve a previously created element by IDgetState()— get current element validation state
Events
keydown— keyboard event listener (altKey, ctrlKey, key, metaKey, shiftKey)
Init options
disableTelemetry— opt out of anonymous usage metricsuseSameOriginApi— route API calls through same origin for latency reductionuseNetworkCheck— validate network connectivity during element mounting
Coming to V3
These features are on the v3 roadmap and will be added in upcoming releases.
- Proxy — proxy HTTP requests (GET/POST/PUT/PATCH/DELETE) through Basis Theory to third-party APIs
- Whitelabel — enhanced whitelabel support for custom-domain element hosting
- Copy Button — secure clipboard button for copying element values, including
enableCopy,copyIconStyles, and thecopyevent - Cobadged — co-branded card network support (e.g., Cartes Bancaires) with
selectedNetworkin change events - Dual Writing — write token data to multiple destinations simultaneously
- Device Fingerprint — browser and device fingerprint collection
Not being ported
- Reveal (
setValue) — programmatically populate an element with previously tokenized data - Custom Card Brand — custom card brand icons and detection logic