SeatingChart mounts the live map canvas without SeatLayer’s cart/tray chrome.
Your application owns totals, tier/GA controls, the checkout button, countdown,
and buyer messages. The
reserved seating SDK and API overview shows
which surface to choose before you commit to the headless route.
import { SeatingChart } from "@seatlayer/js";
const chart = new SeatingChart({
container: "#chart",
event: "ev_9f3a",
publicKey: "pk_test_…",
onSelectionChange: renderCart,
onError: reportPickerError,
});
await chart.render();For a public Platform event, publicKey creates one direct, origin-bound,
Public-sale-only bootstrap; chart geometry and compact inventory status arrive
together, and the resulting bearer remains in memory. For login, presale,
partner, or channel inventory, supply buyerAccessTokenProvider or
buyerAccessToken from your authenticated backend instead. Either explicit
buyer credential takes precedence over publicKey.
Constructor options and callbacks
| Option | Type | Notes |
|---|---|---|
container |
string | HTMLElement |
Required, definite dimensions |
event |
string |
Required event key |
apiBase |
string |
Defaults to production API |
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 } |
One-shot escape hatch; prefer the refreshable provider |
maxSelection |
number |
Default 10 |
selectedObjects |
string[] |
Initial object ids or public labels; applied after live availability loads |
selectableObjects |
string[] | null |
Allow-list of object ids or public labels; omit/null for every free object |
numberOfPlacesToSelect |
number |
Require an exact seated/table guest count and cap selection at that count |
selectionValidators |
PickerSelectionValidator[] |
Minimum-count, consecutive-seat, and no-orphan local guards |
locale / messages |
string / overrides |
Localized on-map copy |
currency |
string |
Display fallback |
colorblindSafe |
boolean |
Accessible palette and hollow booked state |
initialView |
RendererViewMode |
Use flat; perspective modes deprecated |
seatTooltip |
boolean |
Built-in hover tooltip, default true |
onSeatHover |
(details | null) => void |
Build a custom hover surface |
onSelectionChange |
(seats) => void |
Manual, group, or tier change |
onSelectionValidityChange |
(state) => void |
Rule state and typed violations after each selection change |
onSelectionValid / onSelectionInvalid |
callback | Rule-validity transition |
onSelectionLimit |
(max) => void |
A selection attempt reached the active cap |
onHint |
(message | null) => void |
Non-blocking localized advice |
onHold |
(hold) => void |
Any successful hold |
onHoldRestored |
(hold) => void |
resumeHold() succeeds |
onHoldExpired |
() => void |
Server expiry reached |
onGAClick |
(area) => void |
Host should request quantity and call holdGA() |
onDeckTap |
(floorId) => void |
Synchronize custom floor controls |
onError |
(error) => void |
Public async methods report failures here |
Selection and hold methods
| Method | Signature/behavior |
|---|---|
getMode() |
"live" | "test" | null; null before render |
getSelection() |
Current SelectedSeat[] |
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 allow-list without remounting; null restores all free objects |
setMaxSelection(max) |
Change the active selection cap without remounting |
getSelectionValidity() |
Exact-count state, or null when exact selection is disabled |
hold({ ttlMs? }) |
Hold current selection; null on failure/conflict |
resumeHold(holdId) |
Restore without extending |
extendHold(ttlMs?) |
Extend active hold within server limits |
getCurrentHold() |
Current HoldResult | null |
getGAAreas() |
Live GA capacity records |
holdGA(areaId, qty, { tierId?, ttlMs? }) |
Quantity hold |
bestAvailable(qty, categoryKey?, options?) |
Adjacent group hold |
setSeatTier(seatId, tierId) |
Change selected tier; null resets |
release() |
Release the full hold |
releaseLabels(labels) |
Partially release listed labels |
hold(), resumeHold(), holdGA(), and bestAvailable() catch public
transport errors, call onError, and resolve null. Do not wait for an
exception to render buyer recovery.
Controlled and validated selection
const chart = new SeatingChart({
container: "#chart",
event: "ev_9f3a",
publicKey: "pk_test_…",
selectedObjects: ["A-10"],
selectableObjects: ["A-10", "A-11", "A-12"],
selectionValidators: [
{ type: "minimumSelectedPlaces", minimum: 2 },
{ type: "consecutiveSeats" },
{ type: "noOrphanSeats" },
],
onSelectionValidityChange: ({ isValid, remaining, violations }) => {
checkoutButton.disabled = !isValid;
selectionHint.textContent = isValid ? "Ready" : violations.join(", ");
},
});
await chart.render();
chart.selectObjects(["A-11"]);References accept the stable engine id returned on a selected object or the
public inventory label used by hold/book APIs. Unavailable objects are ignored.
Grouped-table references always select or deselect the whole booking unit.
hold() returns null and calls onError before transport while a rule is
invalid. Existing exact-count failures keep code: "selection_count_mismatch";
validator failures use code: "selection_invalid" and include the full
selection state.
View and lifecycle methods
| Method | Behavior |
|---|---|
render() |
Fetch, mount, seed live state; idempotent after success |
getFloors() / setFloor(id) |
Custom multi-floor navigation |
setColorblindSafe(on) |
Runtime accessible palette |
setViewMode(mode) / getViewMode() |
2D renderer compatibility |
zoomIn() / zoomOut() / zoomToFit() |
Host map controls |
destroy() |
Socket/listener/timer/DOM cleanup |
The full 3D buyer experience belongs to SeatPicker; SeatingChart.initialView
does not enable that venue view.
Core result types
interface HoldResult {
holdId: string;
expiresAt: number;
seats?: SelectedSeat[];
items?: HoldLineItem[];
}
interface BestAvailableResult extends HoldResult {
labels: string[];
zoneId?: string;
}
interface GAAreaAvailability {
id: string;
label: string;
capacity: number;
available: number;
categoryKey: string;
price: number;
currency: string;
displayLabel?: string;
displayType?: string;
tiers?: Array<{ id: string; name: string; price: number }>;
}HoldLineItem.unitPrice, currency, tier, and quantity come from SeatLayer, but
your server still re-inspects the hold before payment.
Build the custom cart safely
Render selection
Derive immediate UI from onSelectionChange; keep stable ids separate from
buyer labels.
Create the hold
Call the appropriate hold method when the buyer continues. Disable duplicate submissions while it runs.
Start a visible timer
Count down from server expiresAt, not a locally invented duration.
Hand off only holdId
Your backend inspects authoritative items, runs payment/order logic, and books.
Recover intentionally
Clear stale selection/cart on expiry, show adjacency-specific errors, and
reuse bookingRef for uncertain server retries.
Integration checklist
- The host container has a stable width and height.
- Every callback updates host state idempotently.
-
nullasync results have explicit buyer copy. - GA, tier, best-available, and partial release paths are tested if used.
- Hold time is based on
expiresAt. - Server checkout ignores browser prices.
-
destroy()runs during unmount/navigation. - Keyboard, touch, zoom, and colorblind-safe behavior pass.
Continue with SeatPicker, groups and GA, or best available.