Skip to main content
CheckoutContainer is the plug-and-play checkout: hand it an offer and it renders whichever provider that offer belongs to. Superstate swap, Ondo buy, CoinList token sale - you write one integration, not three. This is the highest rung of the SDK (L3, components) and the one to start from. Reach for a provider-specific container or the hooks below it only when you need markup the SDK does not give you.
Prefer CheckoutContainer over OndoBuyCheckoutContainer or SuperstateSwapCheckoutContainer even when you know the offer type. The provider containers exist, but routing on offer.type yourself means a new provider is your problem; letting CheckoutContainer do it means a new provider is a version bump.

Prerequisites

Render it

That is the whole integration. The container is self-scoped: it renders fully styled with no style provider in the tree.

Props

One mount, one offer, one chain

To render a different offer or chain, remount rather than reassign. Give the container a key that changes with them, as the example above does. offer and chain are read as fixed for the lifetime of the mount. Each provider’s viewmodel owns a step machine - which wallet was authorized, how much is being spent - and that progress is bound to the offer it was made against: a Superstate wallet authorization is allowlisted per offer, and an ERC-20 approval is granted per chain. Changing either prop in place would carry a completed step from one offer onto another. Worse, a swap could land mid-execution, between an approval and the broadcast it was granted for. React discards the old flow’s state along with its component instance, which is the only reset that cannot strand an in-flight transaction.

CheckoutConfig

CheckoutConfig is keyed by OfferType, and every key is required. That is not an oversight. A host that renders CheckoutContainer is saying it will handle whatever offer it is given, so when a new offer type ships, every host breaks at compile time rather than rendering a blank screen their users find first. A new offer type arriving in a catalogue is something you have to decide about. Nothing in the config is per-offer. It is per-integration, so you build one object and pass the same one for every offer you render. defaultCheckoutConfig({ ... }) fills in the entries that have a sensible default, so you only write the ones that do not. Today that is superstate::swap; it exists mostly so a provider that gains an optional setting can be defaulted there rather than appearing in every host’s config object.

Why symbol takes the offer

symbol is a function of the offer rather than a bare value because the symbol is per-asset while the config is per-integration: a host listing AAPLon and TSLAon side by side has one config and two answers. The SDK cannot answer it for you. Ondo’s symbol tracks the underlying ticker and changes on a rebrand, and it already disagrees with offer.asset.code on Sepolia, where a mock asset stands in. If your catalogue does agree, return AssetSymbol(offer.asset.code); if it does not, map the exceptions.
symbol must be total. React hooks cannot be called conditionally, so Ondo’s viewmodel runs for every offer, including Superstate and token-sale ones, where its result is handed to a disabled viewmodel and never read. Throwing on an offer you do not recognise would take down a checkout Ondo has no part in. Return anything.

What happens underneath

Every provider’s viewmodel is called on every render, because React hooks cannot be called conditionally. Only the one matching offer.type is enabled, so the other providers fetch nothing and run no timers. That is why every checkout viewmodel has an enabled flag. The switch on offer.type is exhaustive, and its default branch assigns to const exhaustive: never, so a new offer type is a compile error inside the SDK as well as in your config.
Each product exposes a god viewmodel wrapping its entire flow, plus the smaller hooks it is built from. Call one behind your own markup when you want the SDK’s logic, state and error handling but not its UI.
useSuperstateSwapCheckoutViewModel takes the same options minus ondoSymbol. Both return { state, onEvent }, where state is a discriminated union you switch on exhaustively.If you call these directly you are routing on offer.type yourself, and every hook must be called unconditionally - pass enabled: offer.type === "ondo::swap" to the ones that should be idle. That bookkeeping is exactly what CheckoutContainer does for you.Per-provider detail: Superstate Swap, Ondo Swap Buy, CoinList Token Sale. The conventions the hooks share - enabled, data, and the shape of state - are in SDK structure.
Below the hooks are plain TypeScript namespaces on CoinListClient, with no React and no state. Use them for a non-React app, a custom state layer, or a step the SDK’s flows do not cover.
These return step-tagged results rather than throwing, so you can map each failure to your own copy. The full parameters, progress phases and error steps live on each provider’s page.

Next steps

Superstate Swap

Wallet allowlisting, live quotes, and what Superstate needs from you.

Ondo Swap Buy

The symbol resolver, the committed quote, and the two-call order.

CoinList Token Sale

The render slot, the approval, and tracking participations.

Errors and edge cases

Every error shape the flows return, and how to handle it.