Skip to main content
The Checkout Widget renders a buy-now button directly in your page. When clicked, it opens Henry’s hosted checkout modal where the buyer completes payment, address, and order confirmation. Henry handles everything - you supply the product links and listen for events.

Live Examples


Usage

import Henry from '@henrylabs/js';

const el = Henry.createCheckoutElement('#checkout-container', {
	widgetId: 'widget_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
	items: [
		{
			link: 'https://www.nike.com/t/air-max-270/AH8050-002',
			quantity: 1,
			variant: { size: '10', color: 'Black' },
		},
	],
});
Add a container in your HTML:
<div id="checkout-container"></div>

Options

widgetId string *required

Your Widget ID from the Henry Dashboard.

items CartItem[] *required

One or more products to include in the checkout. Each item requires a link (a direct product page URL) and optionally accepts:
FieldTypeDescription
linkstringDirect product URL
quantitynumberNumber of units. Defaults to 1
variantstring | Record<string, unknown>Variant selection, e.g. { size: "10", color: "Black" }
shippingOption{ id?: string; value?: string }Pre-select a shipping option
couponsstring[]Coupon codes to apply
metadataRecord<string, unknown>Arbitrary metadata passed through to orders
items: [
	{
		link: 'https://www.nike.com/t/air-max-270/AH8050-002',
		quantity: 2,
		variant: { size: '10', color: 'Black' },
		coupons: ['SAVE10'],
		metadata: { source: 'product-page' },
	},
];

display object

Controls how the widget is rendered.

display.size

ValueDescription
"full"Full-width button
"comfortable"Standard-sized button with logo and label
"compact"Smaller button, good for tight layouts
"bubble"Floating circular button
"direct"Opens the checkout iframe directly without a button trigger
display: {
	size: 'compact';
}

display.theme

ValueDescription
"light"Light color scheme
"dark"Dark color scheme
"system"Follows the user’s OS preference
display: {
	theme: 'system';
}

settings CartSettings

Optional settings that control checkout behavior, commission, and event triggers.

settings.options

FieldTypeDescription
allowPartialPurchasebooleanWhether the buyer can remove items and check out with a subset
collectBuyerEmail"off" | "required" | "optional"Controls email collection
collectBuyerAddress"off" | "required" | "optional"Controls address collection
collectBuyerPhone"off" | "required" | "optional"Controls phone collection
settings: {
  options: {
    allowPartialPurchase: true,
    collectBuyerEmail: "required",
    collectBuyerAddress: "optional",
  },
}

settings.commissionFeeFixed

Add a fixed commission fee to every checkout.
settings: {
  commissionFeeFixed: { value: 2.99, currency: "USD" },
}

settings.commissionFeePercent

Add a percentage-based commission fee (0–100).
settings: {
  commissionFeePercent: 5,
}

settings.events

Fire webhooks or send emails on order state changes.
settings: {
  events: [
    {
      type: "order.purchase.full.complete",
      data: [{ type: "send_webhook", webhookUUID: "..." }],
    },
  ],
}

Events

Use .on() to subscribe to checkout events and .off() to remove a listener.

checkout-complete

Fires when the buyer successfully completes checkout.
el.on('checkout-complete', ({ status, order }) => {
	console.log('Status:', status);
	console.log('Order ID:', order?.id);
});
FieldTypeDescription
statusstringFinal order status
orderOrder | undefinedOrder object if available

checkout-closed

Fires when the checkout modal closes, either by the buyer or programmatically.
el.on('checkout-closed', ({ source }) => {
	console.log('Closed by:', source); // "user" | "system"
});

Methods

.on(event, callback)this

Subscribe to a checkout event. Returns the element instance for chaining.
el.on('checkout-complete', handler);

.off(event, callback)this

Remove a previously registered listener.
el.off('checkout-complete', handler);

.destroy()

Unmount the widget and clean up all event listeners. Call this when navigating away or unmounting a component.
el.destroy();

TypeScript

import type { CartItem, CheckoutOptions } from '@henrylabs/js';

const options: CheckoutOptions = {
	widgetId: 'widget_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
	items: [{ link: 'https://www.nike.com/t/air-max-270/AH8050-002', quantity: 1 }],
	display: { size: 'compact', theme: 'dark' },
};

React

Use a ref guard to prevent double-mounting in Strict Mode:
import { useEffect, useRef } from 'react';
import Henry from '@henrylabs/js';

function BuyNow({ productUrl }: { productUrl: string }) {
	const initialized = useRef(false);

	useEffect(() => {
		if (initialized.current) return;
		initialized.current = true;

		const el = Henry.createCheckoutElement('#checkout-container', {
			widgetId: 'widget_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
			items: [{ link: productUrl, quantity: 1 }],
			display: { size: 'comfortable', theme: 'system' },
		});

		el.on('checkout-complete', ({ status, order }) => {
			console.log('Order placed:', order?.id, 'Status:', status);
		});

		return () => el.destroy();
	}, [productUrl]);

	return <div id='checkout-container' />;
}

Card Element

Embed a secure card input for custom payment flows

Server SDK - Checkout

Build headless checkout flows server-side