Skip to content

Build a seating chart in React

Build an interactive React seating chart with the @seatlayer/react npm package, from install to selection, holds, and checkout handoff.

Updated View as Markdown

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 for what happens after that, or the seat map SDK and API overview for how the pieces fit together.

1. Install the package

npm i @seatlayer/react@^0.64.0

For script-tag, browser ESM, plain JavaScript, Vue, and Angular routes, see the install options for all frameworks.

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.

Checkout.tsxtsx
import { SeatPicker } from "@seatlayer/react";

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

3. Authorize the browser

buyerAccessTokenProvider is called with a reason and returns a 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.

Checkout.tsxtsx
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.

4. React to selection and holds

The component takes the same options and callbacks as the SeatPicker reference, 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.
Checkout.tsxtsx
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:

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

Hand off only holdId

Your checkout route receives the hold identity and your own cart context, never a secret key or trusted price.

Build the trusted order on your server

It inspects the active hold, calculates the charge from its returned items, and runs your payment workflow.

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.

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.

headless-chart.jsjs
import { SeatingChart } from "@seatlayer/js";

const chart = new SeatingChart({
  container: "#chart",
  event: "ev_9f3a",
  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.

If you are weighing that against building the map yourself, the JavaScript seating chart guide 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.
  • Platform embeds use buyerAccessTokenProvider; the bse_… token 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, pinned in this tutorial at ^0.64.0. 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 page.

Continue to the Quickstart, or learn the core hold and booking model.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close