Skip to content

SeatPicker reference

Complete buyer widget options, callbacks, methods, checkout handoff, modal mode, 3D, holds, and responsive behavior.

Updated View as Markdown

SeatPicker is the complete buyer experience: live map, confirmation, tier/GA controls, selection tray, hold countdown, best available, 2D/3D, view from seat, mobile bottom sheet, sold-out/closed states, and checkout handoff. For how it sits beside the server and mobile packages, see the seat map API overview.

browser/picker.jsjs
import { SeatPicker } from "@seatlayer/js";

const picker = new SeatPicker({
  container: "#picker",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  onCheckout: (_hold, _seats, handoff) => {
    startCheckout({ holdId: handoff.holdId });
  },
});

await picker.render();

For a public Platform event, publicKey creates one direct, origin-bound, Public-sale-only bootstrap. The response carries chart geometry and compact inventory status together; its short-lived bearer stays in memory. Login, presale, partner, and channel inventory instead use buyerAccessTokenProvider or buyerAccessToken from your authenticated backend. Either explicit buyer credential takes precedence over publicKey.

Choose the surface

SeatPicker SeatingChart
Map canvas Included Included
Cart/tray, CTA, timer, states Included Host builds
Seat confirmation and 360 view Included Host builds
Modal helper SeatPicker.open() No
Best for Complete, styleable buyer flow Fully custom cart and controls

For a buyer purchasing a fixed multi-performance run, use PerformanceGroupPicker instead. Do not pass an Event array to SeatPicker or create several event holds in the browser.

Constructor options

Option Type Default/notes
container string | HTMLElement Required inline; omit for open()
event string Required event key
apiBase string https://api.seatlayer.io
transport PickerTransport Built-in public transport; useful for controlled adapters/tests
operationalTelemetry boolean Omit for built-in production-only timing, true to opt in for another API/custom transport, or false to disable
publicKey string Publishable key for direct, exact-origin, Public-sale-only Platform bootstrap
buyerAccessTokenProvider ({ reason }) => Promise<{ token, expiresAt }> Refreshable private/login/presale/channel access from your authenticated backend; takes precedence over publicKey
buyerAccessToken string | { token, expiresAt } Non-refreshing escape hatch; prefer the provider
maxSelection number 10
selectedObjects string[] Initial object ids or public labels, applied after live availability loads
selectableObjects string[] | null Buyer-selectable allow-list; omit/null for every free object
numberOfPlacesToSelect number Require exactly this many seated/table/GA guest units before checkout
selectionValidators PickerSelectionValidator[] Optional minimum-count, consecutive-seat, and no-orphan sale guards
locale string Browser language → en; built-in en, es, de, fr
messages Record<string,string> Per-message copy overrides
currency string USD fallback; event/org data wins
colorblindSafe boolean false
initialView RendererViewMode Use flat; legacy perspective values are deprecated
enable3D boolean true when WebGL2 is available
max3DSeats number 60,000 desktop; 30,000 on small/low-core devices; host overrides are accepted
onBuyerViewChange ({ view, seatId? }) => void Map/3D entry, exit, and targeted-seat changes
onAnalytics (event, props) => void Optional host-owned Preview load/3D event sink; separate from automatic anonymous load diagnostics
hideBadge boolean Deprecated, no effect: the badge follows the account’s white-label add-on
hideEventDetails boolean Hide duplicate host/event identity while inline; fullscreen restores context
theme SeatPickerTheme Partial host overrides
pricing SeatPickerPricing Buyer display overrides; not server ledger authority
holdTtlMs number Fallback hold duration; an event-dashboard setting takes priority
initialHoldId string Verify/restore an existing event hold
restoreHold boolean true; uses per-event session storage
readOnly boolean Render live inventory without selection, holds, or checkout
confirmSelection boolean true
seatView boolean true
checkout "handoff" | "hosted" handoff; hosted is an explicit managed-commerce opt-in
returnUrl string Validated hosted-checkout return location

Callbacks

Callback Signature Meaning
onCheckout (hold, seats, handoff) => void Hold succeeded and buyer chose checkout
onCheckoutUnavailable ({ reason, handoff }) => void Hosted checkout cannot run; the hold remains available for host handoff
onOrderConfirmed (order) => void Hosted payment was confirmed
onBooked (handoff) => void Realtime state confirms this handed-off hold was booked
onOfferAvailabilityChange (availability) => void Active ticket-release/offer display state changed
onSelectionChange (seats) => void Selection changes
onSelectionValidityChange (state) => void Exact-count state after each selection change
onSelectionValid / onSelectionInvalid callback Exact-count validity transition
onSelectionLimit (max) => void A selection attempt reached the active cap
onHoldChange (hold, seats, handoff) => void Hold created/restored/extended/partially released/released
onHoldExpired () => void Current hold expired and widget reset
onHoldRestored (hold, seats, handoff) => void Prior hold verified and restored
onClose () => void Modal closes
onAccessExpired (state) => void Buyer access session expired; state says whether refresh recovered it
onAccessUnavailable (state) => void Scoped private inventory is no longer available
onSelectedObjectUnavailable (state) => void A selected, unheld object left this buyer’s projection
onError (error) => void Render/transport operation fails

Use the third onCheckout argument for new integrations:

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

interface CheckoutLineItem {
  label: string;
  displayLabel?: string;
  displayType?: string;
  objectId: string;
  objectType: "seat" | "booth" | "ga" | "table";
  categoryKey: string;
  tierId: string | null;
  unitPrice: number;
  currency: string;
  quantity: number;
}

The browser handoff is excellent display context, but payment still uses a fresh server hold inspection.

Scoped channel prices need no browser configuration

When buyerAccessTokenProvider returns a session scoped to a priced sales channel, SeatPicker receives only that session’s compact price projection and uses it for seats, tables, booths, GA quantities, ticket tiers, totals, and the checkout handoff. You do not pass a channel id or duplicate the channel price book in browser options.

The hold endpoint resolves the same channel price independently and freezes it with channelPricingVersion. Continue to treat the inspected hold as the checkout authority. setPricing() remains a host-side presentation override; it cannot replace or weaken the server’s channel price.

Public methods

Method Result
render() Promise<this>
close() Close modal or destroy inline picker
getSelection() Current PickerSeat[]
selectObjects(objects) / deselectObjects(objects) Select/deselect by engine id or public label
clearSelection() Clear every unheld selection
selectCategories(keys) / deselectCategories(keys) Change every selectable object in named categories
setSelectableObjects(objects) Replace the buyer allow-list without remounting
setMaxSelection(max) Change the active order cap without remounting
getSelectionValidity() Rule state, including typed violations, held lines, and pending GA tickets
setMapTheme(map) Repaint the canvas map without remounting or losing selection
setEventDetailsHidden(hidden) Hide/show duplicate host event identity in place
setPricing(pricing) Repaint buyer display prices without changing server-authoritative hold prices
isColorblindSafe() Current accessible palette state
setColorblindSafe(on) Change accessible palette state
setViewMode(mode) / getViewMode() Legacy 2D renderer mode compatibility
getBuyerView() "map" | "venue3d"
setBuyerView(view, { flyToSeatId?, resetView? }) Switch buyer surface, run a targeted 3D tour, or reset the venue overview
getCurrentHold() HoldResult | null
resumeHold(holdId) Verify and restore a hold
removeHeldTicket(label) Remove one held item and keep the rest
bestAvailable(qty, categoryKey?, options?) Hold a server-selected group
release() Release current hold and reset
refreshAccess() Ask the configured provider for a fresh buyer-access session
destroy() Remove DOM, socket, listeners, and timers

For a fixed-size purchase flow, set numberOfPlacesToSelect. For guided buyer selection, add one or more local rules:

selectionValidators: [
  { type: "minimumSelectedPlaces", minimum: 2 },
  { type: "consecutiveSeats" },
  { type: "noOrphanSeats" },
]

consecutiveSeats requires one uninterrupted run in the same row and ticket category. noOrphanSeats prevents a choice from newly stranding one free seat between unavailable neighbours. The built-in CTA stays disabled and shows localized guidance until every rule is satisfied. onSelectionValidityChange exposes { isValid, count, required, remaining, seats, violations } for custom host chrome. The full picker counts seated places, grouped-table guest quantities, held lines, and pending GA quantities together.

const picker = await SeatPicker.open({
  event: "ev_9f3a",
  onCheckout: (_hold, _seats, handoff) => {
    startCheckout({ holdId: handoff.holdId });
  },
  onClose: () => restoreHostUI(),
});

picker.close();

The modal traps focus, closes through Escape/scrim/close button, restores scroll and prior focus, and fires onClose.

Holds and restore

The picker persists the current hold id in sessionStorage by default. On a return visit it verifies that hold against the same event before showing it as owned.

Set restoreHold: false when your router/cart owns persistence, then supply initialHoldId or call resumeHold(). Clear host cart state when onHoldChange(null, …) fires.

The built-in countdown offers an extension in the final minute and resets on confirmed extension. Server limits and renewal caps remain authoritative.

Buyer 3D and analytics

enable3D controls whether the Map/3D affordance is offered. The 3D module is lazy-loaded only after use and falls back to the complete 2D experience when WebGL2 is unavailable.

Use setBuyerView("venue3d", { flyToSeatId }) for a guided tour and onAnalytics to forward Preview journey events to your own system.

With the built-in transport and apiBase: "https://api.seatlayer.io", the picker separately sends one anonymous operational load timing after a successful render. Custom transports, other API origins, and localhost stay silent unless operationalTelemetry: true; false always opts out. The request goes only to the configured apiBase, omits credentials and referrer, and never includes an access token. It is not routed through onAnalytics.

See 3D buyer view and buyer analytics.

Responsive behavior

Layout responds to the picker container, not only the viewport:

  • wide containers show a side panel and docked details;
  • narrow containers use a bottom sheet and touch-first map controls; and
  • fullscreen uses the native API or a platform fallback.

Give the container a definite height. Test the actual embed width, mobile browser chrome, keyboard, reduced motion, and colorblind-safe mode.

Lifecycle checklist

  • Construct once per mounted host view.
  • Await render() and expose failures through onError.
  • Send only holdId to the booking server boundary.
  • Clear host state when the hold disappears.
  • Use stable order ids for booking retries.
  • Call destroy() during application unmount.
  • Test inline/modal, narrow/wide, 2D/no-WebGL, expiry, and conflict paths.

Continue with holds and checkout, customization, or the SeatingChart reference.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close