Framework Adapters
React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Solid, and Angular usage of EmbeddedCheckout.
What every adapter wraps
Every framework adapter is a thin wrapper around one core function, mountEmbeddedCheckout, which mounts the cross-origin checkout frame into an element you own. The props are the same everywhere — only the binding syntax and the event/callback naming change.
Prop
Type
config.apiEndpoint is required — there is no fallback to window.location.origin, because that would send the session token to your own server. Omitting it (and not having set it via configure()) throws MissingEndpointError. publishableKey must match pk_test_ / pk_live_ followed by at least four alphanumerics, or assertPublishableKey throws PublishableKeyError.
Two more rules that apply to every framework on this page:
frameUrlis a base, not a page. Passhttps://pay.example.com/embed; the SDK buildshttps://pay.example.com/embed/checkout.htmlviacheckoutFrameUrl().- Do not pass
onRedirect/@redirectunless you mean it. Omitting it is the correct default — the SDK navigates the top window itself. If you supply a handler you own the navigation, and a handler that only logs will strand the customer mid-3DS. Every adapter detects this: the redirect callback is only wired through when you actually attach one.
Styles are never auto-injected — import @checkout/sdk/styles.css once per app (or @import "@checkout/sdk/tailwind.css" in a Tailwind v4 build).
Per-framework usage
React is the only framework with a full in-page UI (Checkout) in addition to the frame wrapper (EmbeddedCheckout). Both come from @checkout/sdk/react.
import { EmbeddedCheckout } from '@checkout/sdk/react'
import '@checkout/sdk/styles.css'
export function PayPage({ session }: { session: string }) {
return (
<EmbeddedCheckout
source={{ kind: 'checkout', session }}
config={{
apiEndpoint: 'https://api.example.com',
publishableKey: 'pk_test_abcd1234',
}}
frameUrl="https://pay.example.com/embed"
returnUrl={window.location.href}
onSuccess={() => console.log('paid')}
onError={(err) => console.error(err)}
onClose={() => console.log('closed')}
className="my-checkout"
/>
)
}Callback props: onSuccess(), onError(err), onRedirect(url), onClose(). EmbeddedCheckout additionally accepts className and style for the host <div>. The component remounts the frame when source, config, returnUrl, or frameUrl changes — pass stable objects (useMemo) if you re-render often.
For the in-page (non-frame) UI, use Checkout, which takes source, config, and optionally client, embedMode, returnUrl, returnUrlOverride, logLabel, issuerDigitsLookup, plus onSuccess / onError / onRedirect:
import { Checkout } from '@checkout/sdk/react'
import '@checkout/sdk/styles.css'
<Checkout
source={{ kind: 'checkout', session: 'cs_123' }}
config={{ apiEndpoint: 'https://api.example.com', publishableKey: 'pk_test_abcd1234' }}
onSuccess={() => router.push('/thanks')}
/>Checkout also takes issuerDigitsLookup (default false), which adds a field to the installment step where the payer finds their card by its first 6–8 digits. The digits are matched in the browser against the view's card_faces and never sent. Keep it off when Checkout renders in-page on your own site; the hosted checkout, the payment-link page and the embed frame turn it on.
@checkout/sdk/react also exports PaymentElement, SetupIntentElement, BankAccountElement, LinkBadge, and the useCheckout / useLink / resolveCheckoutGate hooks. Besides installmentPlans, useCheckout() returns cardFaces: the view's card_faces (CheckoutCardFace[] with id, provider, name, network, image_url, bin_prefixes), limited to providers present in installment_plans, or [].
Components ship as source
The per-framework components are published as source files — .vue, .svelte, .tsx, .ts — and compiled by your own toolchain. This is the same convention Svelte's svelte-package uses, and it is why the import paths point at a file rather than a barrel.
Needs @vitejs/plugin-vue (Nuxt includes it). Some setups also need the package excluded from pre-bundling so the .vue file reaches the plugin:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
optimizeDeps: { exclude: ['@checkout/sdk'] },
})Shared exports on every framework subpath
./vue, ./nuxt, ./svelte, ./sveltekit, ./angular, and ./solid all export the identical non-component surface, so you can reach the imperative API from any of them:
import {
mountEmbeddedCheckout,
configure,
CheckoutClient,
assertPublishableKey,
PublishableKeyError,
MissingEndpointError,
resolveApiEndpoint,
resolveFrameUrl,
checkoutFrameUrl,
isCheckoutReturnMessage,
buildCheckoutReturnUrl,
applePay,
googlePay,
promptPay,
formatCurrency,
validateCardNumber,
} from '@checkout/sdk/vue'Calling configure() once at app start lets you drop frameUrl from individual mounts; apiEndpoint and publishableKey must still be in config, because that object is what crosses into the frame — call-site values always win:
import { configure } from '@checkout/sdk'
configure({
apiEndpoint: 'https://api.example.com',
frameUrl: 'https://pay.example.com/embed',
publishableKey: 'pk_test_abcd1234',
})./solid is the only one that adds a component (EmbeddedCheckout) to this list. React's surface differs — see the React tab above.