> ## Documentation Index
> Fetch the complete documentation index at: https://docs.passage.coinlist.co/llms.txt
> Use this file to discover all available pages before exploring further.

# React SDK changelog

> Release history for @coinlist-co/react

<div className="max-w-5xl mx-auto px-8">
  <Info>
    This changelog covers the [`@coinlist-co/react`](https://www.npmjs.com/package/@coinlist-co/react) SDK.
  </Info>

  <Update label="v0.10.0" description="July 24, 2026 — Token-sale flow, erc20/tokenSale namespaces, and stricter offer models">
    ### Breaking Changes

    #### Participation methods moved to the `tokenSale` namespace

    The flat participation methods on `CoinListClient` and `CoinListServer` now live under `coinlist.tokenSale`, mirroring how swaps sit under `coinlist.swap`.

    | Before                                     | After                                                |
    | ------------------------------------------ | ---------------------------------------------------- |
    | `coinlist.fetchParticipations(offerId?)`   | `coinlist.tokenSale.fetchParticipations(offerId?)`   |
    | `coinlist.fetchParticipationsPage(params)` | `coinlist.tokenSale.fetchParticipationsPage(params)` |
    | `coinlist.fetchParticipation(id)`          | `coinlist.tokenSale.fetchParticipation(id)`          |
    | `coinlist.createParticipation(params)`     | `coinlist.tokenSale.createParticipation(params)`     |

    **Migration:** insert the `tokenSale` namespace before the method name. The parameters and return types are unchanged.

    ```ts theme={null}
    // Before
    const participations = await coinlist.fetchParticipations(offerId);
    // After
    const participations = await coinlist.tokenSale.fetchParticipations(offerId);
    ```

    #### ERC-20 reads moved from `swap` to a new `erc20` namespace

    `getTokenAllowance` and `getTokenBalance` are generic ERC-20 reads shared across the swap and token-sale flows, so they moved off `coinlist.swap` onto `coinlist.erc20`.

    | Before                                    | After                                      |
    | ----------------------------------------- | ------------------------------------------ |
    | `coinlist.swap.getTokenAllowance(params)` | `coinlist.erc20.getTokenAllowance(params)` |
    | `coinlist.swap.getTokenBalance(params)`   | `coinlist.erc20.getTokenBalance(params)`   |

    **Migration:** replace `swap.getTokenAllowance` with `erc20.getTokenAllowance` and `swap.getTokenBalance` with `erc20.getTokenBalance`. The endpoints and parameters are unchanged.

    #### `Offer.endsAt` and `OfferDetail.endsAt` are now nullable

    An offer can legitimately have no end date, so `endsAt` is now `Date | null` (was `Date`). `fromDto` maps a missing end date to `null`.

    **Migration:** handle the `null` case when reading `endsAt`.

    ```ts theme={null}
    // Before — endsAt was always a Date
    const label = offer.endsAt.toLocaleDateString();
    // After — endsAt may be null
    const label = offer.endsAt ? offer.endsAt.toLocaleDateString() : "No end date";
    ```

    ### Added

    #### Token-sale flow

    A new `tokenSale` namespace encapsulates the on-chain invest flow, mirroring `executeSwap`. On the client, `coinlist.tokenSale.executeTokenSale()` runs a sale end-to-end against a connected `EvmWallet`:

    ```ts theme={null}
    const result = await coinlist.tokenSale.executeTokenSale({
      wallet,
      offerId,
      offerOptionId,
      assetId,
      paymentTokenAddress,    // ERC-20 the user pays with (e.g. USDC/USDT)
      fundingContractAddress, // the approve() spender
      chain,
      amount,                 // BlockchainAmount, base units
      onProgress,             // optional phase callback
    });
    ```

    It reads the current allowance, submits an ERC-20 `approve()` for the sale amount (resetting a stale non-zero allowance to `0` first for USDT-style tokens), waits for it to mine, and records the participation with CoinList. Wallet connection and chain switching remain the host's responsibility. See [Build the invest flow](/sdk/invest-flow) for the end-to-end integration.

    <Note>
      An `approve()` is submitted even when the existing allowance already covers the amount: the backend requires a fresh approval transaction hash on every participation and verifies it on-chain before confirming.
    </Note>

    Failures are returned as typed, step-tagged errors rather than thrown. The reset-to-zero and the main approval are reported distinctly (`allowance-reset` / `allowance-reset-reverted` versus `approval` / `approval-reverted`) so you can tell which of the two wallet prompts the user rejected, and the approval hash is surfaced on a backend failure so recording can be retried without re-approving on-chain. New types: `ExecuteTokenSaleParams`, `TokenSaleExecutionPhase`, `TokenSaleExecutionError`, `TokenSaleExecutionResult`, and the shared `Erc20ApprovalError` (reused across the swap and token-sale flows).

    #### `erc20` namespace

    `coinlist.erc20` exposes the generic ERC-20 reads shared across on-chain flows:

    | Method                    | Endpoint                  |
    | ------------------------- | ------------------------- |
    | `erc20.getTokenAllowance` | `GET /v1/token/allowance` |
    | `erc20.getTokenBalance`   | `GET /v1/token/balance`   |

    #### Offer type

    `Offer` and `OfferDetail` now expose a `type` field (`OfferType`, one of `'sale'` or `'swap'`) so you can distinguish token-sale offers from swap offers. `OfferType` is exported from `@coinlist-co/react/shared`.

    ### Changed

    #### Stricter offer models

    Fields the backend now guarantees as non-null are typed as required (`string` instead of `string | null`):

    * `Offer`: `tagline`, `bannerUrl`, `logoUrl`.
    * `OfferDetail`: `tagline`, `bannerUrl`, `logoUrl`, `category`.

    `OfferDetail.about` stays nullable. `OfferCard` now hides its "Ends" row when an offer has no end date.
  </Update>

  <Update label="v0.9.0" description="July 20, 2026 — Swap namespace, external-wallet connect, and KYC/tax/PII flows">
    This release folds in the 0.7.0 and 0.8.0 development versions.

    ### Added

    #### Swap namespace

    `client.swap` and `server.swap` wrap the swap, token, and wallet endpoints:

    | Method                                 | Endpoint                                  |
    | -------------------------------------- | ----------------------------------------- |
    | `swap.getOutputToken`                  | `GET /v1/swap/output-token`               |
    | `swap.getPreview`                      | `GET /v1/swap/preview`                    |
    | `swap.getStatus`                       | `GET /v1/swap/status`                     |
    | `swap.getTokenAllowance`               | `GET /v1/token/allowance`                 |
    | `swap.getTokenBalance`                 | `GET /v1/token/balance`                   |
    | `swap.getAuthorization`                | `GET /v1/wallet/authorized`               |
    | `swap.allowWallet`                     | `POST /v1/offers/{offer_id}/allow-wallet` |
    | `swap.requestWalletOwnershipChallenge` | `POST /v1/wallet-ownership`               |

    On the client, the namespace also drives a caller-supplied wallet:

    * `authorizeWallet({ wallet, offerId, contractAddress, chain, onProgress })` — proves wallet ownership and allow-lists it for a swap offer, with typed progress phases and a `success` / `error` result. Idempotent: returns success immediately if the wallet is already authorized.
    * `executeSwap({ wallet, contractAddress, chain, inputTokenAddress, quote, slippageBps, onProgress })` — runs the full on-chain swap: status check, ERC-20 approval (with USDT-style allowance reset), swap submission, and confirmation. The confirmed output is decoded from the `Swapped` event.

    `viem ^2` is a new peer dependency.

    #### Swap hooks

    * `useSwapOutputToken` — reads the swap contract's output token (the fund share).
    * `useSwapQuote` — polls a live quote (15s default), skipping until the output-token decimals and a valid amount are known.
    * `useSwapTokenBalances` — polls the user's balance for one or more input assets.

    #### Wallet abstraction

    `EvmSigner` / `EvmWallet` interfaces plus typed `WalletError` classification — bring your own wallet stack (wagmi, viem, Privy, …). New phase and error types: `WalletAuthorizationPhase`, `WalletAuthorizationError`, `SwapExecutionPhase`, `SwapExecutionError`, `SwapExecutionResult`.

    See [Build the swap flow](/sdk/swap-flow) for the end-to-end integration.

    #### External wallet connect

    Prove and bind an external wallet to an offer option:

    * `createWalletOwnershipChallenge(params)` → `POST /v1/wallet-ownership`
    * `connectExternalWallet(offerId, params)` → `POST /v1/offers/{offer_id}/addresses`
    * `listOptionAddresses(offerId, offerOptionId)` → `GET /v1/offers/{offer_id}/addresses`
    * `removeOptionAddress(offerId, addressId)` → `DELETE /v1/offers/{offer_id}/addresses/{id}`

    UI: `ConnectWalletModal` and `ConnectedWalletList` (list, change, and remove bound wallets), with the `useConnectWallet` and `useOptionAddresses` hooks.

    #### Identity, KYC, and tax documents

    * `createKycToken(levelName?, reset?)` → `POST /v1/kyc-token` (Sumsub)
    * `fetchPii()` → `GET /v1/pii`, pre-fill data for tax forms
    * `submitDocument(documentType, fields)` → `POST /v1/documents/{document_type}/submission`
    * Components `IdentityVerification` and `TaxDocumentModal`; hooks `useKycToken` and `useTaxDocument`.
    * `handleRequirement` now resolves the `kyc_approved`, `identity_verified`, `proof_of_address`, `source_of_funds`, `accreditation`, `external_wallet`, `whitelisted_wallet`, `document`, and `jurisdiction` requirement types.

    <Note>
      `identity_verified`, `proof_of_address`, and `source_of_funds` have since been removed from the API. The SDK still handles them, but offers no longer return them, so you do not need to write branches for those three.
    </Note>

    #### App-level authentication

    `CoinListServer.clientCredentialsOAuth()` performs the OAuth 2.0 `client_credentials` grant for app-level access without a user session. The server offer reads (`fetchOffers`, `fetchOffersPage`, `fetchOfferDetails`, `fetchOfferRequirements`) accept an optional `clientCreds` argument to use it.

    #### Shared (`@coinlist-co/react/shared`)

    * `BlockchainAmount` (with `add` / `sub`), `parseBlockchainAmount`, `formatAmount`, `computeSlip`, and the `Bps` newtype.
    * Constants and helpers: `DEFAULT_SLIPPAGE_BPS`, `SLIPPAGE_OPTIONS_BPS`, `SWAP_POLL_INTERVAL_MS`, `SUPERSTATE_SWAP_CONTRACT_ADDRESS_SEPOLIA`, `TOKEN_REGISTRY`, `USDC_SYMBOL`, `txExplorerUrl`.
    * Swap, KYC, PII, document-submission, and wallet-ownership types.

    ### Changed

    #### `CoinListProvider` is safe to mount app-wide

    The provider is now an alias of `CoinListContextProvider` and renders no DOM or styles. Components self-scope their styling through `CoinListStyleScope`, so mounting the provider high in your tree no longer affects the rest of the app.
  </Update>

  <Update label="v0.6.0" description="July 2, 2026 — Required approvalTransactionHash, stricter wallet type, CSS isolation">
    ### Breaking Changes

    #### `approvalTransactionHash` is required on `createParticipation`

    `CreateParticipationParams` now requires `approvalTransactionHash` — the hash of the ERC-20 allowance transaction that precedes the participation. Submit the approval on-chain first, then pass its hash.

    ```ts theme={null}
    await coinlist.createParticipation({
      offerId,
      offerOptionId,
      chain,
      walletAddress,
      amount,
      assetId,
      approvalTransactionHash, // ← now required
    });
    ```

    #### `WalletAddress` narrowed to `` `0x${string}` ``

    `WalletAddress` is now the template-literal type `` `0x${string}` `` instead of `string`. Values that aren't `0x`-prefixed literals need validation or a cast at the boundary.

    ### Added

    #### CSS isolation

    SDK styles are isolated behind a prefixed Tailwind build with scoped provider injection, and context is separated from styling so the context functions can be used without pulling in SDK styles. This work completes in v0.9.0, where `CoinListProvider` becomes safe to mount app-wide.

    #### `coinlist.co` support

    Added first-class support for the `coinlist.co` environment.
  </Update>

  <Update label="v0.5.1" description="May 7, 2026 — Read-only SessionStore support">
    ### Breaking Changes

    #### `SessionStore.setSession` is now optional

    `setSession` has been changed from a required method to an optional one (`setSession?`). This formalises **read-only store** mode for execution contexts — such as Next.js Server Components — that can read cookies but cannot write them.

    Previously the only workaround was a no-op `setSession: async () => {}`, which silently discarded refreshed sessions after consuming the refresh token over the network, effectively causing a silent logout. Omitting `setSession` is now the explicit, safe contract: the SDK skips token refresh entirely, making no network calls and consuming no refresh tokens.

    **Migration:** if you currently pass a no-op `setSession`, remove it. If your store is writable, no change is needed.

    ```ts theme={null}
    // Before — no-op workaround (caused silent logout bug)
    createCoinListServer({
      sessionStore: {
        getSession: () => getServerSession(),
        setSession: async () => {}, // ← remove this
      },
    });

    // After — read-only store (safe, explicit)
    createCoinListServer({
      sessionStore: {
        getSession: () => getServerSession(),
      },
    });
    ```

    ### Added

    #### `WritableSessionStoreRequiredError`

    New error class exported from `@coinlist-co/react/server`. `completeOAuth()` and `logout()` throw this immediately when called on a read-only store (no `setSession`), before any network call is made.

    ### Fixed

    #### No unnecessary network retries on expired tokens in read-only mode

    Previously, when a request returned a 401 and the store was read-only, the SDK would retry the request with the same expired token — wasting a round-trip that always failed. The SDK now detects the read-only store and surfaces the 401 immediately without retrying.
  </Update>

  <Update label="v0.5.0" description="May 5, 2026 — API consistency, new /shared entry point, and Base* component removal">
    ### Breaking Changes

    #### Client methods renamed: `fetchAll*` → `fetch*`

    `fetchAllOffers` and `fetchAllParticipations` have been renamed on both `CoinListClient` and `CoinListServer` to drop the redundant `All` prefix.

    | Before                                            | After                                          |
    | ------------------------------------------------- | ---------------------------------------------- |
    | `CoinListClient.fetchAllOffers()`                 | `CoinListClient.fetchOffers()`                 |
    | `CoinListClient.fetchAllParticipations(offerId?)` | `CoinListClient.fetchParticipations(offerId?)` |
    | `CoinListServer.fetchAllOffers()`                 | `CoinListServer.fetchOffers()`                 |
    | `CoinListServer.fetchAllParticipations(offerId?)` | `CoinListServer.fetchParticipations(offerId?)` |

    **Migration:** do a global find-and-replace in your codebase:

    ```
    fetchAllOffers(    →  fetchOffers(
    fetchAllParticipations(  →  fetchParticipations(
    ```

    #### Hooks renamed: `useCoinList*` → `use*`

    The `CoinList` infix has been dropped from all hook names and their associated option/result/reason types.

    | Before                               | After                        |
    | ------------------------------------ | ---------------------------- |
    | `useCoinListOffers`                  | `useOffers`                  |
    | `UseCoinListOffersOptions`           | `UseOffersOptions`           |
    | `UseCoinListOffersResult`            | `UseOffersResult`            |
    | `useCoinListOfferDetails`            | `useOfferDetails`            |
    | `UseCoinListOfferDetailsOptions`     | `UseOfferDetailsOptions`     |
    | `UseCoinListOfferDetailsResult`      | `UseOfferDetailsResult`      |
    | `useCoinListRequirements`            | `useRequirements`            |
    | `UseCoinListRequirementsOptions`     | `UseRequirementsOptions`     |
    | `UseCoinListRequirementsResult`      | `UseRequirementsResult`      |
    | `useCompleteCoinListOAuth`           | `useCompleteOAuth`           |
    | `UseCompleteCoinListOAuthOptions`    | `UseCompleteOAuthOptions`    |
    | `CompleteCoinListOAuthFailureReason` | `CompleteOAuthFailureReason` |

    **Migration:** rename the hook calls and any imported types. Example:

    ```tsx theme={null}
    // Before
    import { useCoinListOffers, useCoinListRequirements } from '@coinlist-co/react';
    const { offersState } = useCoinListOffers({ serverOffers: data });

    // After
    import { useOffers, useRequirements } from '@coinlist-co/react';
    const { offersState } = useOffers({ data });
    ```

    #### SSR prop renamed: `serverOffers` / `serverData` → `data`

    The SSR pre-fetch prop has been unified to `data` across all hooks and components that accept server-side data. The `RequirementsServerData` type is also renamed to `RequirementsData`.

    | Before                                    | After                               |
    | ----------------------------------------- | ----------------------------------- |
    | `UseOffersOptions.serverOffers`           | `UseOffersOptions.data`             |
    | `UseOfferDetailsOptions.serverData`       | `UseOfferDetailsOptions.data`       |
    | `UseParticipationsOptions.serverData`     | `UseParticipationsOptions.data`     |
    | `UseRequirementsOptions.serverData`       | `UseRequirementsOptions.data`       |
    | `OffersGrid` prop `serverOffers`          | `OffersGrid` prop `data`            |
    | `RequirementsChecklist` prop `serverData` | `RequirementsChecklist` prop `data` |
    | `RequirementsServerData` type             | `RequirementsData` type             |

    **Migration:** rename the prop/option to `data` wherever you pass pre-fetched server results.

    #### `LoadRequirementsState` CONTENT shape: `requirementsByOptionId` → `requirements`

    If you read the `CONTENT` state returned by `useRequirements` (or previously `useCoinListRequirements`) directly, the field name has changed.

    ```tsx theme={null}
    // Before
    if (requirementsState.type === 'CONTENT') {
      const byOption = requirementsState.requirementsByOptionId;
    }

    // After
    if (requirementsState.type === 'CONTENT') {
      const byOption = requirementsState.requirements;
    }
    ```

    #### `Base*` components removed

    `BaseOffersGrid`, `BaseOffersGridProps`, `BaseRequirementsChecklist`, and `BaseRequirementsChecklistProps` have been removed. The connected components (`OffersGrid`, `RequirementsChecklist`) now accept all the same customization props directly — including the optional `data` prop for pre-fetched server data — so there is no longer a need for a separate base variant.

    **Migration:** replace `BaseOffersGrid` and `BaseRequirementsChecklist` usage with the main components and pass the same props directly.

    #### Import paths restructured — new `@coinlist-co/react/shared` entry point

    Sub-path exports (`/client`, `/client/hooks`, `/client/components`, `/client/core`) have been removed. The package now exposes three canonical entry points:

    | Entry point                 | Contents                                                                                              |
    | --------------------------- | ----------------------------------------------------------------------------------------------------- |
    | `@coinlist-co/react`        | React client — components, hooks, `CoinListClient`                                                    |
    | `@coinlist-co/react/server` | Node/SSR — `CoinListServer`                                                                           |
    | `@coinlist-co/react/shared` | **New** — isomorphic domain types (`Offer`, `Participation`, `Requirement`, errors, pagination, etc.) |

    Domain types that were previously re-exported from both `@coinlist-co/react` and `@coinlist-co/react/server` are now the sole responsibility of `@coinlist-co/react/shared`. They remain re-exported from the client and server entry points as well, so most imports will continue to work without changes. However, if you were importing from the now-deleted sub-paths, update your imports:

    ```ts theme={null}
    // Before (sub-path imports — no longer valid)
    import { CoinListClient } from '@coinlist-co/react/client';
    import { useOffers } from '@coinlist-co/react/client/hooks';

    // After
    import { CoinListClient, useOffers } from '@coinlist-co/react';
    ```

    #### `OffersGridProps` type renamed from `Props`

    The exported type for `OffersGrid` props was the generic name `Props`. It is now exported as `OffersGridProps`.

    ```ts theme={null}
    // Before
    import type { Props } from '@coinlist-co/react';

    // After
    import type { OffersGridProps } from '@coinlist-co/react';
    ```
  </Update>

  <Update label="v0.4.0" description="April 30, 2026 — SSR data fetching, useParticipations hook, and requirement action defaults">
    ### Added

    #### Server (`@coinlist-co/react/server`)

    * `CoinListServer` now exposes the full data-fetching surface previously only available on `CoinListClient`: `fetchAllOffers()`, `fetchOffersPage()`, `fetchOfferDetails()`, `fetchAllParticipations()`, `fetchParticipationsPage()`, `fetchParticipation()`, `createParticipation()`, `fetchOfferRequirements()`, and `fetchRequirementStatuses()`. Use these in Next.js Route Handlers and Server Components without shipping any client bundle.
    * `@coinlist-co/react/server` now re-exports all shared types — `Offer`, `OfferDetail`, `Participation`, `Requirement`, `RequirementStatusInfo`, pagination helpers, `NotAuthenticatedError`, and related types — so you no longer need to import them from the client entry.

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * `useParticipations(offerId?)` hook — loads all participations for the authenticated user with a `LOADING / CONTENT / ERROR` state machine, optionally filtered by offer. New exported types: `UseParticipationsResult`, `LoadParticipationsState`, `LoadParticipationsReason`.
    * `handleRequirement(requirement)` on `CoinListClient` — opens the corresponding CoinList page for completing a requirement in a new tab (`/verify-identity`, `/wallet`, etc.). No-op for `jurisdiction` requirements.
    * `contactSupport()` on `CoinListClient` — opens the CoinList support ticket page in a new tab.

    ### Changed

    #### Components

    * `RequirementsChecklist`: `onRequirementAction` and `onContactSupport` props have been renamed to `onRequirementActionOverride` and `onContactSupportOverride`. Both now **default** to `CoinListClient#handleRequirement` and `CoinListClient#contactSupport` respectively, so most integrations can omit them entirely. Pass `null` to disable the corresponding button/link.
    * `RequirementItem`: `onAction` and `onContactSupport` now accept `null` (in addition to `undefined`) to suppress rendering of action buttons.

    ### Removed

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * `sandbox` option removed from `ClientConfig`. Sandbox offers are no longer toggled via the SDK config.
    * `StaticRequirementsChecklistProps` type removed. Use `RequirementsChecklist` with the connected API or `BaseRequirementsChecklist` for fully custom rendering.
  </Update>

  <Update label="v0.3.0" description="April 23, 2026 — Requirements API, sandbox mode, and participations filtering">
    ### Added

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * `fetchOfferRequirements(offerId)` on `CoinListClient` — returns requirements grouped by option ID (`Record<OfferOptionId, Requirement[]>`).
    * `fetchRequirementStatuses(offerId)` on `CoinListClient` — returns the authenticated user's status for each requirement.
    * `useCoinListRequirements(offerId)` hook for loading requirements and statuses with a `LOADING / CONTENT / ERROR` state machine.
    * `sandbox` option on `ClientConfig` — when `true`, passes `sandbox=true` to offers API calls so sandbox offers are included.
    * New exported types: `Requirement`, `RequirementId`, `RequirementType`, `RequirementStatusValue`, `RequirementStatusInfo`, `ParticipationsPaginationParams`.
    * New exported enums/constants: `RequirementVariant`, `RequirementStatus`, `ChecklistStatus`.
    * New hook types: `UseCoinListRequirementsResult`, `LoadRequirementsState`, `LoadRequirementsReason`.
    * `fetchAllParticipations(offerId?)` and `fetchParticipationsPage(params)` now accept an optional `offerId` to fetch participations for a specific offer. `fetchParticipationsPage` takes the new `ParticipationsPaginationParams` type (superset of `PaginationParams`) which carries the `offerId` field.

    ### Fixed

    #### Components

    * Fixed missing dark mode CSS variables in Next.js projects — prebuilt styles now include the full set of design-system tokens so components render correctly under `dark` class or `prefers-color-scheme: dark`.
  </Update>

  <Update label="v0.2.0" description="April 13, 2026 — Participations API">
    ### Added

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * `fetchAllParticipations()` on `CoinListClient` for fetching all participations across pages.
    * `fetchParticipationsPage()` on `CoinListClient` for paginated participation fetching.
    * `fetchParticipation()` on `CoinListClient` for fetching a single participation by id.
    * `createParticipation()` on `CoinListClient` for creating a new participation.
    * New exported types: `Participation`, `ParticipationId`, `ParticipationStatus`, `CreateParticipationParams`, `Blockchain`, `WalletAddress`.
  </Update>

  <Update label="v0.1.2" description="April 8, 2026 — Support UI component customization">
    ### Added

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * Added support for passing `className` and `containerClassName` to `<OffersGrid />`, `<OfferCard />`, and `<CoinListSignInCard />`.
  </Update>

  <Update label="v0.1.1" description="April 7, 2026 — Publish missing exports">
    ### Fixed

    #### Client (`@coinlist-co/react`, `@coinlist-co/react/client`)

    * Re-export offer, offer detail, and pagination types from the package root and client core entry so you can type against `Offer`, `OfferDetail`, pagination params, and related helpers without reaching into internal modules.
    * Export `BaseCoinListSignInCard` and `BaseCoinListSignInCardProps` from `@coinlist-co/react/client` for custom sign-in layouts built on the same primitives as `CoinListSignInCard`.
  </Update>

  <Update label="v0.1.0" description="April 2026 — Offers UI and OAuth helpers">
    ### Added

    #### Client (`@coinlist-co/react/client`)

    * `fetchAllOffers()`, `fetchOffersPage()`, and `fetchOfferDetails()` on `CoinListClient` for fetching offers data.
    * `<OffersGrid />` — batteries-included component that loads offers and displays them in a responsive grid, with `<OfferCard />` for each tile.
    * `useCoinListOffers()` hook for loading offers when you want full control over layout instead of the grid.
    * `useCoinListOfferDetails()` hook for a single offer’s details.
    * `useCompleteCoinListOAuth()` hook to run the OAuth redirect callback once on mount.
    * Optional `authorizationPageUrl` on client config so you can point `startOAuth()` at a non-production authorization page.
  </Update>

  <Update label="v0.0.1" description="March 2026 — Initial release">
    ### Added

    #### Client (`@coinlist-co/react/client`)

    * `CoinListProvider` and `useCoinList()` hook for React context-based SDK initialization.
    * `createCoinListClient(config)` factory for manual or non-React usage.
    * OAuth 2.0 with PKCE via `startOAuth()` and `completeOAuth()`.
    * `getAuthState()` and `logout()`.

    #### Server (`@coinlist-co/react/server`)

    * `createCoinListServer(config)` factory for BFF / Next.js API routes.
    * `completeOAuth()`, `accessToken()` (auto-refreshing), and `logout()`.

    #### Components

    * `CoinListSignInCard` and `CoinListSignInButton` for OAuth sign-in flows.
    * `RequirementsChecklist` / `RequirementItem` — eligibility checklist with accordion UI.
    * `PoweredByCoinList` attribution badge.
    * Prebuilt CSS — no `tailwindcss` peer dependency required.

    #### Infrastructure

    * Subpath exports: `@coinlist-co/react`, `@coinlist-co/react/client`, `@coinlist-co/react/server`.
    * Requires React 18+.
  </Update>
</div>
