Prerequisites
CoinListProviderwith workinggetAccessToken(session established on your backend)- User signed in: your
getAccessTokenreturns a non-null access token after OAuth (see Set up OAuth authentication)
Recommended: OffersGridContainer
OffersGridContainer uses useOffers internally and renders a responsive grid of offer cards with loading, error, and empty states.
maxColumns- upper bound for the grid when space allows (default3)loading,error,emptyState- replace default slots with your own React nodesonOfferClick- when set, cards become interactive; omit for read-only listsdata- pre-fetched offers fromCoinListServer’scoinlist.offers.list()for SSR. Pass them in to skip the client-side request on first render.
useOffersGridViewModel() yourself and render OffersGridView (or your own markup) against the state it returns.
Server-side pre-fetch
Every hook and container that fetches acceptsdata. Pass it and the component uses that data as-is and skips the initial client-side fetch - the user sees offers in the first paint instead of a spinner.
This is the SSR path. Your Server Component calls CoinListServer, and the browser renders with the answer already in hand:
Use a read-only session store in Server Components: they can read cookies but not write them, so a writable store would silently consume a refresh token. See read-only session stores.
useOffers({ data })) and on the other data-backed containers, including RequirementsChecklistContainer. Where a hook exposes refetch(), the seed answers the initial render only - an explicit refetch still goes to the network.
coinlist.offers.list() on the server also takes an optional app-level token from coinlist.auth.clientCredentials(), so a public catalogue page can render with no user session at all.
Going lower
Open these when you need custom layouts or non-React code.Build your own UI with hooks (L2)
Build your own UI with hooks (L2)
Use Pass
useOffers from @coinlist-co/react when you want full control over markup but still prefer reactive loading state.The hook exposes offersState:LOADING- provider not ready or request in flightERROR-reasonisnot-authenticatedorgeneric-errorCONTENT-offersis every page of offers merged (same data ascoinlist.offers.list())
data if you have server-fetched offers to hydrate from: useOffers({ data }).Drive it yourself with the client (L1)
Drive it yourself with the client (L1)
Inside React you usually reach these through For imperative, non-hook usage, create a
useCoinList():coinlist.offers.list()- iterates every paginated page and returns all offers (throwsNotAuthenticatedErrorif the user is not logged in)coinlist.offers.listPage(params)- one page of API results when you implement your own pagination UI
CoinListClient with createCoinListClient from @coinlist-co/react. Server code (Next.js Route Handlers, Server Components) should use the equivalent coinlist.offers.list() on CoinListServer from @coinlist-co/react/server instead - on the server it also takes an optional app-level token from coinlist.auth.clientCredentials(), so it can run with no user session.Raw HTTP
Raw HTTP
The SDK calls the same API documented in the API reference. Use raw HTTP only if you are not using React, or you need fields or flows the SDK does not expose yet. You still need a valid Bearer access token from your OAuth session.
Token display metadata
Offers carry chain and address pairs inoffer.tokens, but not the name, symbol, decimals or logo you need to render them. Those come from CoinList’s public token registry through coinlist.tokens.
TokenMetadata carries identifier, name, symbol, decimals, logo and logoDark. logo is a union: VECTOR is a single SVG url, while RASTER carries an original plus webp variants at 32 to 512px - pick the smallest variant that covers your render size, or original if none does.
coinlist.tokens is the one unauthenticated namespace: no method needs a logged-in user, and reads go to the registry’s CDN rather than the CoinList API. It works with no session at all, and with a read-only server session store.
coinlist.tokens.get(token)returns metadata for one token, ornullwhen the registry does not list it. The registry is curated, sonullis a normal answer: fall back to your own display defaults rather than treating it as an error.coinlist.tokens.list(chain)returns every token the registry lists for a chain, in one request. Prefer it over callinggetin a loop when rendering a catalogue - two hundred tokens is still a single snapshot download. A missing chain snapshot throws rather than returning[], because its absence is a deployment problem and not an empty catalogue.
useTokenMetadata takes enabled and data like the other data hooks, and exposes refetch(). A data of null is a valid seed meaning “not in the registry”, and is distinct from leaving data undefined.
Next step
Display offer details
Load the full
OfferDetail for an id from the grid, which is what checkout takes.