Documentation Index Fetch the complete documentation index at: https://docs.henrylabs.ai/llms.txt
Use this file to discover all available pages before exploring further.
The Card Element renders a secure card input form inside an iframe. Card data never touches your page - it’s collected entirely within the iframe, then tokenized. You receive a short-lived cardToken to pass to your server for payment processing.
This element is the standard way to collect card details in a Headless Checkout flow.
Usage
import Henry from '@henrylabs/js' ;
const cardEl = Henry . createCardElement ( '#card-container' );
Add a container in your HTML:
< div id = "card-container" ></ div >
To submit, call validate() then submit():
const isValid = await cardEl . validate ();
if ( isValid ) {
const { cardToken } = await cardEl . submit ();
// send cardToken to the server SDK
}
Options
styles CardStyles
Optional. Customize the appearance of the card input fields.
const cardEl = Henry . createCardElement ( '#card-container' , {
styles: {
cardNumber: {
base: {
color: '#111' ,
fontSize: '16px' ,
fontFamily: 'Inter, sans-serif' ,
},
'::placeholder' : {
color: '#aaa' ,
},
invalid: {
color: '#e53e3e' ,
},
},
cardExpiration: {
base: { color: '#111' },
},
cardVerification: {
base: { color: '#111' },
},
},
});
CardStyles reference
Field Type Description cardNumberCardElementStyleStyles for the card number field cardExpirationCardElementStyleStyles for the expiration date field cardVerificationCardElementStyleStyles for the CVV/CVC field containerCSSPropertiesStyles applied to the outer container element inputWrappersCSSPropertiesStyles applied to each field wrapper section1CSSPropertiesStyles applied to the first row (card number) section2CSSPropertiesStyles applied to the second row (expiry + CVV) fontsstring[]Font URLs to load inside the iframe
CardElementStyle reference
Each field style (cardNumber, cardExpiration, cardVerification) accepts:
Field Type Description baseCardElementStyleVariantDefault state styles completeCardElementStyleVariantStyles when the field is filled and valid invalidCardElementStyleVariantStyles when the field contains an error emptyCardElementStyleVariantStyles when the field is empty containerCardElementStyleVariantStyles for the field’s inner container placeholderstringCustom placeholder text labelstringCustom label text labelContainerCSSPropertiesStyles for the label element fontsstring[]Field-specific font URLs
CardElementStyleVariant supports these CSS properties:
{
backgroundColor ?: string
color ?: string
fontFamily ?: string
fontSize ?: string
fontStyle ?: string
fontWeight ?: string | number
letterSpacing ?: string
lineHeight ?: string | number
padding ?: string
textAlign ?: string
textDecoration ?: string
textShadow ?: string
textTransform ?: string
// Pseudo-class overrides
":hover" ?: { ... }
":focus" ?: { ... }
":read-only" ?: { ... }
"::placeholder" ?: { ... }
"::selection" ?: { ... }
":disabled" ?: { ... }
}
Methods
validate() → Promise<boolean>
Returns true if all card fields contain valid input. Use this before submitting to surface validation errors to the buyer.
const isValid = await cardEl . validate ();
if ( ! isValid ) {
// show your own error message
}
submit() → Promise<{ cardToken: string }>
Tokenizes the card and returns a cardToken. Pass this token to your server to complete the payment - never send raw card data from your frontend.
const { cardToken } = await cardEl . submit ();
await fetch ( '/api/pay' , {
method: 'POST' ,
body: JSON . stringify ({ cardToken }),
headers: { 'Content-Type' : 'application/json' },
});
destroy()
Removes the iframe and cleans up all event listeners. Call this when navigating away or unmounting a component.
Full example
< form id = "payment-form" >
< div id = "card-container" ></ div >
< button type = "submit" > Pay now </ button >
</ form >
< script type = "module" >
import Henry from '@henrylabs/js' ;
const cardEl = Henry . createCardElement ( '#card-container' , {
styles: {
cardNumber: {
base: { color: '#111' , fontSize: '16px' },
'::placeholder' : { color: '#aaa' },
invalid: { color: '#e53e3e' },
},
fonts: [ 'https://fonts.googleapis.com/css2?family=Inter&display=swap' ],
},
});
document . getElementById ( 'payment-form' ). addEventListener ( 'submit' , async ( e ) => {
e . preventDefault ();
const isValid = await cardEl . validate ();
if ( ! isValid ) return ;
const { cardToken } = await cardEl . submit ();
await fetch ( '/api/pay' , {
method: 'POST' ,
body: JSON . stringify ({ cardToken }),
headers: { 'Content-Type' : 'application/json' },
});
});
</ script >
React
import { useEffect , useRef } from 'react' ;
import Henry from '@henrylabs/js' ;
function CardForm () {
const cardElRef = useRef < ReturnType < typeof Henry . createCardElement > | null >( null );
const initialized = useRef ( false );
useEffect (() => {
if ( initialized . current ) return ;
initialized . current = true ;
cardElRef . current = Henry . createCardElement ( '#card-container' , {
styles: {
cardNumber: {
base: { color: '#111' , fontSize: '16px' },
},
},
});
return () => cardElRef . current ?. destroy ();
}, []);
async function handleSubmit ( e : React . FormEvent ) {
e . preventDefault ();
if ( ! cardElRef . current ) return ;
const isValid = await cardElRef . current . validate ();
if ( ! isValid ) return ;
const { cardToken } = await cardElRef . current . submit ();
// send cardToken to the server SDK
}
return (
< form onSubmit = { handleSubmit } >
< div id = 'card-container' />
< button type = 'submit' > Pay now </ button >
</ form >
);
}
Checkout Widget Embed a complete buy-now checkout button and modal
Server SDK - Checkout Build headless checkout flows server-side