Get OAuth credentials
You need values from Passage before you write code:client_id- public identifier for your appredirect_uri- exact callback URL registered with Passage (must match what you pass in the SDK)client_secret- server-only; never expose this to the browser or commit it to frontend bundles
COINLIST_CLIENT_SECRET), and use NEXT_PUBLIC_ only for values that are safe in the client (typically client_id and redirect_uri).
Install the SDK
react and react-dom ^18 || ^19, and viem ^2. React 20 is outside the supported range.
The package is ESM-only. Bundlers such as Next.js, Vite and webpack consume it natively. In Node it requires ^20.19.0 || >=22.12.0, the versions that can require() an ES module, so CommonJS code on those versions can still load it.
See Package overview for all import paths and the full namespace map.
How the SDK is organized
The SDK splits three ways, in this order: environment, then level, then feature. Learn those three and you can find anything in it.Level: how much you want to build
Every feature is available at several levels. Lower levels provide more control but require more implementation. Start with L3 components and use L2 or L1 when you need more control.-
L3 - React components (highest). You get UI and behavior together, working. You provide the input, plus an optional config object, and you are done.
<CheckoutContainer offer optionId chain wallets />is an entire purchase flow: wallet selection, amount entry, approval, review, confirmation.optionIdis the offer option the user chose, and it is required on every checkout: the SDK never picks an option for you. Others:<OffersGridContainer>,<RequirementsChecklistContainer>,<ClaimContainer>,<CoinListSignInCardContainer>. -
L2 - React hooks (mid). You get the behavior: data fetching, state, polling, error handling, and the on-chain sequencing. You provide the UI.
useOffers()hands you aLOADING / ERROR / CONTENTstate machine and you render whatever you like from it;useOndoBuyCheckoutViewModel()runs the whole Ondo flow behind your own markup. -
L1 - TypeScript clients (low). You get the logic: typed API calls, domain mapping, and total on-chain flows that return a step-tagged result instead of throwing. You build the state management and the UI.
PassageClientin the browser,PassageServeron your backend, no React anywhere. Use it for a non-React app, a custom state layer, or full control.
CoinListSignInButton, OfferAssetCard and OfferSaleCard. RequirementsChecklistView is also still exported, as a loading placeholder, but is due to be removed, and PassageStyleScope is the styling wrapper every SDK component already puts around itself.
Environment: where code runs
Environment comes first in the split because it is a security boundary, not a convenience:PassageServer holds your client_secret, and it must never reach a browser bundle. That boundary is exactly the three import paths you type.
Not every feature is available at every level. L2 and L3 exist only in the browser, because a backend renders nothing. L1 is the only level present everywhere, which is why
passage.offers.list() reads the same on your server as in your React tree.
Feature
Inside each level, the code is grouped by feature, and the same features appear at every rung: auth, offers, requirements, and the provider journeys checkout and claim. Sopassage.requirements (L1), useRequirements() (L2) and <RequirementsChecklistContainer> (L3) are three abstraction levels for the same feature.
Checkout and claim are grouped one level deeper, by provider and product - Superstate swap, Ondo buy and sell, Centrifuge buy and sell, CoinList token sale - because every provider’s flow differs in roughly a fifth of its substance. At L1 each provider has its own namespace (passage.superstate, passage.ondo, passage.centrifuge, passage.tokenSale). <CheckoutContainer> and <ClaimContainer> are what save you from caring, so you integrate once. <CheckoutContainer> routes on offer.type (and, for Ondo and Centrifuge, on a side prop). <ClaimContainer> takes one async offer or a list of any mix of them, and each offer’s type decides which provider reads it, with every row in one card.
Next step
SDK structure
The patterns every part of the SDK follows: namespaces, containers, hook state, and flows that return instead of throwing. Skippable if you would rather start building.
OffersGridContainer, then Display offer details for a single offer. Then Requirements for what a user must satisfy before participating, Wallets to plug in your wallet stack, and Building the Checkout flow to render the purchase itself. Use the API reference when you need raw request and response details.