---
title: "Build a seating chart in React"
description: "Build an interactive React seating chart with the @seatlayer/react npm package, from install to selection, holds, and checkout handoff."
---

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

# Build a seating chart in React

This tutorial builds an interactive seating chart in React with the
`@seatlayer/react` npm package: a live seat map with selection, temporary holds,
and a checkout handoff to your own backend. It uses the released `SeatPicker`
component, so the map, selection tray, hold countdown, and mobile layout come
with it — you write the parts that belong to your product.

Test mode is free with no time limit and no card, so you can complete every step
below before a live account exists. See
[SeatLayer pricing](https://seatlayer.io/pricing/) for what happens after that,
or the [seat map SDK and API overview](https://seatlayer.io/developers/) for how
the pieces fit together.

## 1. Install the package

```sh
npm install @seatlayer/react@0
pnpm add @seatlayer/react@0
yarn add @seatlayer/react@0
bun add @seatlayer/react@0
```

For script-tag, browser ESM, plain JavaScript, Vue, and Angular routes, see the
[install options for all frameworks](/buyer-sdk/install).

## 2. Render the seating chart

The React wrapper exports `SeatPicker`. Give it an event key and an explicit
size — the SDK is container-responsive, so its mount element needs a definite
width and height.

```tsx title="Checkout.tsx"
import { SeatPicker } from "@seatlayer/react";

export function Checkout() {
  return (
    <SeatPicker
      event="ev_9f3a"
      publicKey="pk_test_…"
      style={{ width: "100%", height: 640 }}
      onCheckout={(_, __, handoff) => {
        beginCheckout(handoff.holdId);
      }}
    />
  );
}
```

> **Public sale starts directly**
>
> For a public Platform event, `publicKey` lets the SDK call SeatLayer directly.
> SeatLayer verifies the event, key mode, and exact registered browser origin,
> then returns a Public-only in-memory bearer with the chart and compact
> inventory status in one bootstrap response. The public key is publishable;
> never put the matching `sk_…` secret in React code.

## 3. Authorize private audiences

Skip this step when every buyer sees only Public sale. For login, presale,
partner, or channel inventory, `buyerAccessTokenProvider` is called with a reason
and returns a scoped token and expiry from your own authenticated backend. Keep
the function outside the component (or in a `useCallback`) so it is not recreated
on every render.

```tsx title="Checkout.tsx"
import { SeatPicker } from "@seatlayer/react";

async function getSeatLayerBuyerAccess({ reason }) {
  const response = await fetch("/api/seatlayer/buyer-access", {
    method: "POST",
    credentials: "same-origin",
    cache: "no-store",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ reason }),
  });
  if (!response.ok) throw new Error("Unable to open seat selection");
  return response.json();
}

export function Checkout() {
  return (
    <SeatPicker
      event="ev_9f3a"
      style={{ width: "100%", height: 640 }}
      buyerAccessTokenProvider={getSeatLayerBuyerAccess}
      onCheckout={(_hold, _seats, handoff) => {
        startCheckout({ holdId: handoff.holdId });
      }}
    />
  );
}
```

The `bse_…` token stays in memory. No `sk_…` credential belongs in a client
bundle. If both `publicKey` and a provider/token are supplied, the explicit buyer
credential takes precedence and the SDK never falls back to anonymous Public
sale.

## 4. React to selection and holds

The component takes the same options and callbacks as the
[SeatPicker reference](/buyer-sdk/seat-picker), passed as props. Two are useful
almost immediately:

- `onSelectionChange` — `(seats) => void`, fires whenever the selection changes.
- `onHoldChange` — `(hold, seats, handoff) => void`, fires when a hold is
  created, restored, extended, partially released, or released.

```tsx title="Checkout.tsx"
const [seats, setSeats] = useState([]);

return (
  <SeatPicker
    event="ev_9f3a"
    style={{ width: "100%", height: 640 }}
    buyerAccessTokenProvider={getSeatLayerBuyerAccess}
    onSelectionChange={setSeats}
    onError={reportPickerError}
    onCheckout={(_hold, _seats, handoff) => {
      startCheckout({ holdId: handoff.holdId });
    }}
  />
);
```

`SeatPicker` already renders its own selection tray and hold countdown, so treat
this state as context for the rest of your page rather than a cart you have to
build.

## 5. Hand off to your checkout

A hold temporarily protects inventory while a buyer completes checkout. It is
not a sale. The Buyer SDK creates the hold; your server inspects and books it.

The third `onCheckout` argument is the handoff:

```ts
interface CheckoutHandoff {
  holdId: string;
  expiresAt: number;
  currency: string;
  lineItems: CheckoutLineItem[];
  total: number;
}
```

1. **Hand off only holdId**

   Your checkout route receives the hold identity and your own cart context,
   never a secret key or trusted price.
2. **Build the trusted order on your server**

   It inspects the active hold, calculates the charge from its returned items,
   and runs your payment workflow.
3. **Start a visible timer from server time**

   Count down from server `expiresAt`, not a locally invented duration.

The browser handoff is excellent display context, but payment still uses a fresh
server-side inspection. The complete flow, including release and restore, is in
[holds and checkout handoff](/buyer-sdk/holds-and-checkout).

## 6. Optional: go headless with SeatingChart

`@seatlayer/react` also exports `SeatingChart`, the headless seating canvas
without SeatLayer's cart/tray chrome. Reach for it only when your application
owns totals, tier/GA controls, the checkout button, countdown, and buyer
messages.

```js title="headless-chart.js"
import { SeatingChart } from "@seatlayer/js";

const chart = new SeatingChart({
  container: "#chart",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  onSelectionChange: (seats) => {
    updateYourSelectionUI(seats);
  },
});

await chart.render();
```

Choose `SeatPicker` unless your application has a clear reason to own selection
controls, confirmation, hold timing, pricing presentation, and mobile behavior.
Full options are in the
[SeatingChart headless reference](/buyer-sdk/seating-chart).

If you are weighing that against building the map yourself, the
[JavaScript seating chart guide](https://seatlayer.io/guides/javascript-seating-chart/)
walks through what a hand-built chart still leaves you owning.

## Before you ship

- [ ] The package loads without browser errors.
- [ ] The mount container has an explicit usable size.
- [ ] A test event renders.
- [ ] Selection changes appear immediately.
- [ ] Checkout produces a `holdId`.
- [ ] No `sk_…` credential exists in the client bundle.
- [ ] Public Platform embeds use `publicKey`; scoped private audiences use a
      provider/token, and every `bse_…` bearer stays in memory.
- [ ] Narrow mobile and keyboard behavior are usable.

## Questions

### Which React seating chart library should I use?

It depends on how much of the buyer experience you want to own. `SeatPicker`
gives you the complete flow — map, confirmation, pricing, selection tray, holds,
expiry, success state, mobile layout, and optional 3D — and is the fastest
production route. `SeatingChart` gives you only the live map canvas, and your
product builds the cart, CTA, timer, and states around it. Building on a generic
canvas or charting library instead means you also own venue geometry, live
inventory, hold expiry, and concurrency, which is the part that is hard to keep
correct.

### Is there a React seat picker on npm?

Yes — `@seatlayer/react`, installed here from the current `0.x` release line. It provides
native `SeatPicker` and `SeatingChart` components, and re-exports the
framework-agnostic `SeatPickerWidget` modal and `attachPickerFrame` helper from
`@seatlayer/js`.

### Is there a seats.io alternative for React?

SeatLayer publishes a native React SDK for reserved seating, covering the
complete picker and the headless chart on the same event and live inventory
model. For a feature-by-feature view, read the
[seats.io alternative](https://seatlayer.io/seats-io-alternative/) page.

Continue to the [Quickstart](/start/quickstart), or learn the
[core hold and booking model](/start/how-it-works).

Source: https://docs.seatlayer.io/buyer-sdk/react-seating-chart/index.mdx
