Skip to content

BookingMap, SeatMap, hooks · Browser (client components)

@openseat/react

React bindings for OpenSeat: SeatMap, BookingMap and hooks.

  • 5 components
  • 5 hooks
  • 10 functions
  • 16 interfaces
  • 4 types
  • 5 constants
  • MIT
npm i @openseat/react @openseat/core konva react react-dom
first npm release pending

Install commands show the public npm names. The first npm release is pending; the packages are free and MIT licensed.

depends on @openseat/booking, @openseat/core, @openseat/renderer-konva, @openseat/theme-default · peers konva, react, react-dom

Import
import {
  BookingMap,
  SeatMap,
  useInventory,
  useHold,
  AreaQuantityPicker,
} from "@openseat/react";

Components5

component

AreaQuantityPicker

Ticket quantity picker for one zone. See the module comment.

function AreaQuantityPicker(
  props: AreaQuantityPickerProps,
): JSX.Element;

Props · AreaQuantityPickerProps

PropTypeDescription
areaBookableArea

The zone, as onAreaSelect reports it.

priceEachoptionalstring

Display price per ticket ("₹1,500"). Without it no price or total is shown.

remainingnumber

Places still on sale.

maxnumber

Most tickets the buyer may add now: the places left within their ticket limit. At least 1.

currencyoptionalstring

ISO 4217 currency of the price ("INR"), for the total.

onAdd(quantity: number) => void

Called with a whole number from 1 to max when the buyer presses "Add to cart".

onClose() => void

Called on Escape, the close button and a click on the backdrop.

tieroptionalstring

Ticket type name shown under the zone name ("Gold").

initialQuantityoptionalnumber

Quantity to start from. Default 1.

Default 1

formatAmountoptional(amount: number) => string

Formats the total. Default: Intl.NumberFormat in currency, else the price's own symbol.

addLabeloptionalReactNode

Content of the add button. Default "Add to cart".

Default "Add to cart"

erroroptionalReactNode

A problem to show above the add button, from the host.

rangeMessageoptional(quantity: number) => ReactNode

Message for a typed quantity outside 1..max. Default "Choose between 1 and {max} tickets."

inputPropsoptionalOmit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type"> & Readonly<Record<`data-${string}`, string>>

Extra attributes for the quantity field, such as a test ID.

presentationoptional"auto" | "sheet" | "popover"

"auto" (default): a bottom sheet at ≤ 640px, else a popover.

Default "auto"

Every member is readonly.

@openseat/react/src/AreaQuantityPicker.tsx

component

BookingMap

Seat map for booking flows: draws host statuses, applies selection rules, and offers keyboard and screen-reader access.

Keyboard: Tab into the map, arrow keys move between seats, Enter or Space selects (a seat of a bookAsWhole table goes to onAreaSelect instead, exactly as a click does), + and − zoom, Home goes to the first seat. With sectionZoom, the zoomed-out overview focuses sections instead: arrows move between them, Enter zooms in, and Escape returns to the overview.

Touch: a tap on a seat at least 24px across selects it; a tap on a smaller seat zooms in until seats are that big (and shows its card), and the next tap selects. GA areas, booths and whole tables marked Not for sale (or sold out) in statuses, by their inventory key, cannot be chosen.

const BookingMap: ForwardRefExoticComponent<
  BookingMapProps & RefAttributes<BookingMapHandle>
>

Props · BookingMapProps

PropTypeDescription
layoutLayoutDocument
statusesoptionalStatusesInput

Inventory from your backend, keyed by public seat ID. Missing seats are available.

selectedSeatIdsoptionalreadonly string[]

Controlled selection. Omit for uncontrolled use with defaultSelectedSeatIds.

defaultSelectedSeatIdsoptionalreadonly string[]
onSelectionChangeoptional(selection: readonly string[], change: SelectionChange) => void
reservedSeatIdsoptionalreadonly string[]

Seats the current customer holds, or is in the middle of holding. The inventory reports them as "held" like anyone else's, but they are drawn as selected and never dropped from the selection. Include seats as soon as the hold request starts: backends often push the status change before the hold response arrives.

onSelectionRejectedoptional(violation: SelectionViolation) => void

Called when a click is refused, with the reason (also announced to screen readers).

rulesoptionalSelectionRules
themeoptionalTheme
legendoptionalboolean

Show the colour legend. Default true.

Default true

summaryoptionalboolean

Show the selected-seat summary. Default true.

Default true

renderActionsoptional(info: { readonly selection: readonly string[]; readonly valid: boolean; }) => ReactNode

Rendered under the summary, for a checkout button. Receives the validation result.

accessibleListoptional"hidden" | "visible" | "none"

The accessible seat list, a text alternative to the canvas. hidden (default) keeps it available to screen readers only; visible shows it.

labeloptionalstring

Accessible name of the map. Default "Seat map".

Default "Seat map"

mapHeightoptionalCSSProperties["height"]

Height of the map area. Default 480px.

Default 480px

classNameoptionalstring
styleoptionalCSSProperties
sectionZoomoptionalboolean | "auto" | SectionZoomOptions

Overview-to-section navigation for large venues. Below the overview scale (and only when the layout has sections), clicking a seat or a section zooms to that section instead of selecting; once zoomed in, seats behave normally. An "Overview" button and Escape zoom back out. The overview scale defaults to 1.6 times the fit-to-screen scale.

"auto" (the default, unless hideSeatsInOverview is set) turns it on only for layouts with more than 2,500 seats, so smaller venues show their seats at fit zoom and select on the first click.

accessibleFilteroptionalboolean | "always"

Show a "Show accessible seats" toggle that highlights wheelchair and companion seats and dims the rest. Default: on when the layout has any. "always": also when it has none, as a note saying so ("No wheelchair seats listed for this event"), so a buyer looking for them gets an answer instead of silence.

mapOverlayoptionalReactNode

Drawn over the map, inside its frame (top right): for example zoom buttons. Positioned by the host's CSS (.os-booking-overlay).

extraItemsoptionalreadonly BookingExtraItem[]

Tickets in the basket that are not seats (GA places, booths, whole tables), for example the non-seat lines of buildCart. The summary counts them ("4 tickets selected of 10 maximum") and seat clicks count them towards the per-buyer limit (maxTickets, else maxSeats).

initialViewoptional"auto" | "fit"

Where the map opens. "auto" (default): containers under 600px wide open on the most valuable section (highest price, else most seats) when there are sections, else on the whole venue with seats at least 12px; wider ones fit the whole venue. "fit": always the whole venue.

seatCardoptionalboolean

Show a small card about the seat under the mouse or the last tapped seat ("Row C, Seat 5 · Stalls · ₹200 · Wheelchair space"). Default true.

Default true

notForSaleLabeloptionalstring

The words for places that are not for sale, in the key, the seat card and the list of areas. Default "Not for sale"; a buyer's page may say "Reserved", so they never read as sold out.

onAreaSelectoptional(area: BookableArea) => void

A GA area, a booth with an inventory key, or a bookAsWhole table (or one of its seats) was clicked. There is no default UI: show your own quantity picker. When set, the seats of bookAsWhole tables report their table here instead of being selected one by one.

categoryPricesoptionalReadonly<Record<string, string>>

Display-only price text per category ID. The legend lists the layout's categories as "VIP · £45", and the summary uses it for seats whose status record has no priceLabel (a per-seat priceLabel always wins).

hideSeatsInOverviewoptionalboolean

In the zoomed-out overview, hide individual seats and draw each section filled with its category colour (sold-out sections greyed); zooming into a section reveals its seats. Implies the overview click-to-zoom of sectionZoom, since hidden seats cannot be clicked. Default false.

Default false

legendStyleoptional"auto" | "zones" | "seats"

Which key the legend shows. "seats": the seat states (Available, Selected, Held, Booked) and the price categories. "zones": just "Available" (a swatch of the zones' ticket-type colours) and "Sold out", plus "Not for sale" when something is blocked, as on a zone map. "auto" (default): "zones" when the layout draws no seat that is sold on its own (only zones, booths and zone sections), else "seats".

areaStatusesoptionalreadonly AreaStatusRecord[] | ReadonlyMap<string, AreaStatusRecord>

Availability of zones, booths and whole tables, from the adapter's getAreaStatuses or subscribeAreas. A zone with remaining: 0 counts as sold out even when its status says "available". These merge into statuses by inventory key, so a record there works too.

Every member is readonly.

Ref handle: BookingMapHandle

Example

Tickets.tsx
import { useState } from "react";
import type { LayoutDocument } from "@openseat/core";
import { createRestAdapter } from "@openseat/adapter-rest";
import { BookingMap, useHold, useInventory } from "@openseat/react";

// Create the adapter once (module scope or useState), never inline in render.
const api = createRestAdapter({
  baseUrl: `${location.origin}/api`,
  credentials: "same-origin",
  realtime: { kind: "sse", url: (id) => `${location.origin}/api/v1/events/${encodeURIComponent(id)}/stream` },
});

export function Tickets({ layout, eventId }: { layout: LayoutDocument; eventId: string }) {
  const { statuses } = useInventory(api, eventId); // live over server-sent events
  const hold = useHold(api, eventId, { maxTickets: 6, layout });
  const [selection, setSelection] = useState<readonly string[]>([]);

  return (
    <>
      <BookingMap
        layout={layout}
        statuses={statuses}
        selectedSeatIds={selection}
        onSelectionChange={(next) => setSelection(next)}
        rules={{ maxSeats: 6, preventOrphans: true }}
        categoryPrices={{ vip: "₹1,499", stalls: "₹499", balcony: "₹299" }}
      />
      <button disabled={!hold.canHold || selection.length === 0} onClick={() => hold.hold(selection)}>
        Hold {selection.length} seats
      </button>
      {hold.state.status === "held" ? <p>Held for {Math.ceil(hold.remainingMs / 1000)} s</p> : null}
    </>
  );
}

Recipe: booking map over REST

@openseat/react/src/BookingMap.tsx

component

SeatLegend

Colour key for seat states. States also have markers, so the legend shows those too. With categories, the price categories follow, with their prices.

function SeatLegend(props: SeatLegendProps): JSX.Element;

Props · SeatLegendProps

PropTypeDescription
themeoptionalTheme

Default defaultTheme

statesoptionalreadonly SeatVisualState[]

Default SEAT_VISUAL_STATES

categoriesoptionalreadonly Category[]

Price categories to list after the states, each with its colour swatch.

categoryPricesoptionalReadonly<Record<string, string>>

Display-only price text per category ID, shown next to the category name ("VIP · £45").

accessibleoptionalboolean

Adds an "Accessible seat" entry with the wheelchair pictogram the map draws on those seats.

Default false

companionoptionalboolean

Adds a "Companion seat" entry with the companion mark the map draws on those seats.

Default false

notForSaleoptional{ readonly seats?: boolean; readonly areas?: boolean; }

What is marked Not for sale. When given, the "blocked" state is not listed among the states; a "Not for sale" entry follows instead, with a crossed seat when seats are blocked and a hatched square when GA areas, booths or whole tables are. Neither: no entry.

notForSaleLabeloptionalstring

The words for not-for-sale places: "Not for sale" by default; a buyer's page may say "Reserved".

Default LEGEND_LABELS.notForSale

variantoptional"seats" | "zones"

"zones": the key of a zone map, just "Available" (a swatch in the tierColours) and "Sold out", plus "Not for sale" when notForSale says so. states, categories, accessible and companion are ignored. Default "seats".

Default "seats"

tierColoursoptionalreadonly string[]

Ticket-type colours for the zones "Available" swatch, at most 6 used. Default: the available seat colour.

Every member is readonly.

@openseat/react/src/BookingMap.tsx

component

SeatMap

Controlled seat map. The host owns layout, seat states and selection; the map draws them and reports interaction through callbacks. Nothing the user does changes what is drawn until the host updates the props. Draws with SeatMapRenderer, so edit mode has resize and rotate handles.

const SeatMap: ForwardRefExoticComponent<
  SeatMapProps & RefAttributes<SeatMapHandle>
>

Props · SeatMapProps

PropTypeDescription
layoutLayoutDocument

The layout to draw. Update it immutably: changed objects are found by reference.

seatStatesoptionalSeatStatesInput

Visual state per public seat ID. Seats not listed are available.

selectedObjectIdsoptionalreadonly string[]

Object IDs drawn with a selection outline.

focusedObjectIdoptionalstring | null

Object drawn with a keyboard focus ring and scrolled into view.

modeoptionalRendererMode
themeoptionalTheme
panOnDragoptionalboolean

Edit mode only: make drags pan, like a hand tool.

drawOnDragoptionalboolean

Edit mode only: make drags report onDrawStart/onDraw/onDrawEnd, for drawing tools.

snapMoveoptional(input: { readonly objectIds: readonly string[]; readonly delta: Point; readonly modifiers?: MoveModifiers; }) => Point
guidesoptionalreadonly Guide[]

Alignment guide lines to draw, in layout coordinates.

cursoroptionalstring | null

Cursor override, for example crosshair while placing objects.

resolveTargetoptional(hitId: string) => string | null

Maps the object under the pointer to what a drag moves; null makes it not draggable. Default: the hit itself.

marqueeOnAreasoptionalboolean

Edit mode: dragging on an unselected area box-selects instead of moving it. Default true.

Default true

focusScopeoptionalreadonly string[] | null

Objects inside the entered scope; everything else is dimmed and not hittable. Null or absent: no scope.

issueHighlightsoptionalreadonly string[]

Objects outlined as having validation issues.

vertexEditingIdoptionalstring | null

Object whose outline shows vertex handles.

rowHandlesIdoptionalstring | null

Row that shows "+" handles to extend or shorten it.

gridoptionalGridOptions

Edit-mode grid. Hidden by default.

wheelModeoptionalWheelMode

What a plain mouse wheel does. Default "zoom".

Default "zoom"

seatVisibilityoptionalSeatVisibility

"hidden-below-scale" hides individual seats in the zoomed-out overview. Default "always".

Default "always"

seatHideScaleoptionalnumber

Scale below which seats are hidden with seatVisibility="hidden-below-scale". Default 0.5.

Default 0.5

areaFillModeoptionalAreaFillMode

"solid" fills coloured sections and GA areas with their colour instead of a tint. Default "tint".

Default "tint"

zoneNotesoptionalReadonlyMap<string, string>

View mode: a second line under each GA zone's name (its price), by inventory key.

zoomLimitsoptionalPartial<ZoomLimits>
seatLabelMinScaleoptionalnumber
rowLabelMinScaleoptionalnumber
hitToleranceoptional{ readonly mouse?: number; readonly touch?: number; }
isHittableoptional(obj: LayoutObject, mode: RendererMode) => boolean
initialViewportoptionalViewport
classNameoptionalstring
styleoptionalCSSProperties
containerPropsoptionalOmit<HTMLAttributes<HTMLDivElement>, "className" | "style" | "children">

Extra attributes for the container element, such as aria-describedby.

onObjectClickoptionalmethod(event: ObjectClickEvent) => void

From RendererEvents

onBackgroundClickoptionalmethod(event: BackgroundClickEvent) => void

From RendererEvents

onHoverChangeoptionalmethod(objectId: string | null) => void

From RendererEvents

onViewportChangeoptionalmethod(viewport: Viewport) => void

From RendererEvents

onMoveStartoptionalmethod(event: { readonly objectIds: readonly string[]; }) => void

From RendererEvents

onMoveoptionalmethod(event: MoveEvent) => void

Fires during a drag, for live feedback such as alignment guides. Do not commit changes here.

From RendererEvents

onMoveEndoptionalmethod(event: MoveEvent) => void

Fires once per drag gesture. Commit the move here, then call setLayout.

From RendererEvents

onTransformEndoptionalmethod(event: TransformEndEvent) => void

From RendererEvents

onGroupTransformoptionalmethod(event: GroupTransformEvent) => void

Live group transform (once per frame at most). Do not commit here.

From RendererEvents

onGroupTransformEndoptionalmethod(event: GroupTransformEvent) => void

Commit a group transform here.

From RendererEvents

onMarqueeSelectoptionalmethod(event: MarqueeSelectEvent) => void

From RendererEvents

onDrawStartoptionalmethod(event: DrawEvent) => void

Drawing drags, while setDrawOnDrag(true) is on. onDrawEnd fires once, unless the drag is cancelled.

From RendererEvents

onDrawoptionalmethod(event: DrawEvent) => void

From RendererEvents

onDrawEndoptionalmethod(event: DrawEvent) => void

From RendererEvents

onDrawCanceloptionalmethod() => void

A drawing drag was interrupted (for example by a second finger starting a pinch).

From RendererEvents

onPointerMoveoptionalmethod(event: { readonly point: Point; readonly pointerType: PointerKind; }) => void

The mouse or pen moved with no button pressed. Point in layout coordinates.

From RendererEvents

onContextMenuoptionalmethod(event: ContextMenuEvent) => void

Secondary click (or long press) in edit mode. The browser menu is suppressed.

From RendererEvents

onVertexDragoptionalmethod(event: VertexDragEvent) => void

A vertex handle is being dragged (once per frame at most).

From RendererEvents

onVertexDragEndoptionalmethod(event: VertexDragEvent) => void

From RendererEvents

onVertexInsertoptionalmethod(event: VertexInsertEvent) => void

A midpoint handle was clicked.

From RendererEvents

onVertexRemoveoptionalmethod(event: VertexRemoveEvent) => void

Alt+click or double-click on a vertex handle.

From RendererEvents

onRowHandleDragoptionalmethod(event: RowHandleDragEvent) => void

A row's "+" handle was dragged far enough to change its seat count.

From RendererEvents

onRowHandleDragEndoptionalmethod(event: RowHandleDragEvent) => void

From RendererEvents

onRowHandleCanceloptionalmethod() => void

From RendererEvents

Every member is readonly.

Also accepts the members of RendererEvents, listed above.

Ref handle: SeatMapHandle

@openseat/react/src/SeatMap.tsx

component

SelectionSummary

Selected seats with prices and anything stopping checkout (too few seats, stranded seats…).

function SelectionSummary(
  props: SelectionSummaryProps,
): JSX.Element;

Props · SelectionSummaryProps

PropTypeDescription
layoutLayoutDocument
statusesoptionalStatusesInput
selectionreadonly string[]
rulesoptionalSelectionRules
onRemoveoptional(seatId: string) => void
categoryPricesoptionalReadonly<Record<string, string>>

Display-only price text per category ID, used for seats whose status record has no priceLabel. The seat's event category (from its status record) wins over the layout's.

extraItemsoptionalreadonly BookingExtraItem[]

Non-seat tickets in the basket. When given, the count is in tickets ("4 tickets selected of 10 maximum") and these lines are listed after the seats.

childrenoptionalReactNode

Every member is readonly.

@openseat/react/src/BookingMap.tsx

Hooks5

hook

useHold

Hold lifecycle for one event, with a live countdown.

function useHold(
  adapter: BookingAdapter,
  eventId: string,
  options?: UseHoldOptions,
): UseHoldResult;

Parameters

ParameterTypeDescription
adapterBookingAdapter
eventIdstring
optionsoptionalUseHoldOptions

Default {}

Returns

Example

Tickets.tsx
import { useState } from "react";
import type { LayoutDocument } from "@openseat/core";
import { createRestAdapter } from "@openseat/adapter-rest";
import { BookingMap, useHold, useInventory } from "@openseat/react";

// Create the adapter once (module scope or useState), never inline in render.
const api = createRestAdapter({
  baseUrl: `${location.origin}/api`,
  credentials: "same-origin",
  realtime: { kind: "sse", url: (id) => `${location.origin}/api/v1/events/${encodeURIComponent(id)}/stream` },
});

export function Tickets({ layout, eventId }: { layout: LayoutDocument; eventId: string }) {
  const { statuses } = useInventory(api, eventId); // live over server-sent events
  const hold = useHold(api, eventId, { maxTickets: 6, layout });
  const [selection, setSelection] = useState<readonly string[]>([]);

  return (
    <>
      <BookingMap
        layout={layout}
        statuses={statuses}
        selectedSeatIds={selection}
        onSelectionChange={(next) => setSelection(next)}
        rules={{ maxSeats: 6, preventOrphans: true }}
        categoryPrices={{ vip: "₹1,499", stalls: "₹499", balcony: "₹299" }}
      />
      <button disabled={!hold.canHold || selection.length === 0} onClick={() => hold.hold(selection)}>
        Hold {selection.length} seats
      </button>
      {hold.state.status === "held" ? <p>Held for {Math.ceil(hold.remainingMs / 1000)} s</p> : null}
    </>
  );
}

Recipe: booking map over REST

@openseat/react/src/hooks.ts

hook

useInventory

Loads seat statuses from an adapter and keeps them fresh with its real-time subscription, if it has one. Requests are cancelled on unmount.

function useInventory(
  adapter: InventoryAdapter,
  eventId: string,
  options?: { readonly seatIds?: readonly string[] },
): UseInventoryResult;

Parameters

ParameterTypeDescription
adapterInventoryAdapter
eventIdstring
optionsoptional{ readonly seatIds?: readonly string[]; }

Default {}

Returns

Example

Venue.tsx
import { useState } from "react";
import { createLayout, generateStraightRows } from "@openseat/core";
import { createRestAdapter } from "@openseat/adapter-rest";
import { BookingMap, useInventory } from "@openseat/react";

const layout = createLayout({ id: "hall", name: "Main hall", width: 1000, height: 800 });
layout.objects = generateStraightRows({
  idPrefix: "stalls",
  rowCount: 10,
  seatsPerRow: 20,
  origin: { x: 100, y: 100 },
  seatSpacing: 28,
  rowSpacing: 32,
  aisleAfter: [10],
}).objects;

const api = createRestAdapter({ baseUrl: "https://api.example.com" });

export function Venue() {
  const { statuses } = useInventory(api, "show-2026-11-20");
  const [selection, setSelection] = useState<readonly string[]>([]);
  return (
    <BookingMap
      layout={layout}
      statuses={statuses}
      selectedSeatIds={selection}
      onSelectionChange={(next) => setSelection(next)}
      rules={{ maxSeats: 8, preventOrphans: true }}
    />
  );
}

Recipe: booking map over REST

@openseat/react/src/hooks.ts

hook

useLayout

Validates a layout from an object, a JSON string, or a loader function (for example () => fetch(url).then(r => r.json())). Invalid layouts are reported in errors and never rendered.

function useLayout(source: LayoutSource): UseLayoutResult;

Parameters

ParameterTypeDescription
sourceLayoutSource

Returns

@openseat/react/src/hooks.ts

hook

useSeatSelection

Local seat selection with rules applied. Seats that become unavailable (for example from a real-time update) are dropped automatically.

function useSeatSelection(
  options: UseSeatSelectionOptions,
): UseSeatSelectionResult;

Parameters

ParameterTypeDescription
optionsUseSeatSelectionOptions

options takes UseSeatSelectionOptions:

Returns

@openseat/react/src/hooks.ts

Functions10

function

areaCardText

Card text for a GA area, booth or whole table: "Booth B7 · ₹2,000 · Not for sale".

function areaCardText(
  area: BookableArea,
  statuses: StatusInput,
  categoryPrices?: Readonly<Record<string, string>>,
): string;

Parameters

ParameterTypeDescription
areaBookableArea
statusesStatusInput
categoryPricesoptionalReadonly<Record<string, string>>

Returns

string

@openseat/react/src/booking-view.ts

function

categoryLegendText

Price text for a category, as "VIP · £45", or just the name when it has no price.

function categoryLegendText(
  name: string,
  price: string | undefined,
): string;

Parameters

ParameterTypeDescription
namestring
pricestring | undefined

Returns

string

@openseat/react/src/BookingMap.tsx

function

formatQuantityTotal

A total in the price's currency: Intl.NumberFormat when currency is a code it knows, else the amount with the price text's own prefix and suffix ("₹" + 4,500). Whole amounts have no decimals.

function formatQuantityTotal(
  amount: number,
  currency: string | undefined,
  priceEach: string | undefined,
): string;

Parameters

ParameterTypeDescription
amountnumber
currencystring | undefined
priceEachstring | undefined

Returns

string

@openseat/react/src/AreaQuantityPicker.tsx

function

parsePriceLabel

The number in a display price ("₹1,200" → 1200, "£45.50" → 45.5, "€12,50" → 12.5). Null when the text has no number ("Free" → null).

function parsePriceLabel(text: string | undefined): number | null;

Parameters

ParameterTypeDescription
textstring | undefined

Returns

number | null

@openseat/react/src/booking-view.ts

function

seatCardText

One line about a seat for the hover or tap card, as buyers read it: "Row C, Seat 5 · Stalls · ₹200 · Wheelchair space". Built from labels only, never IDs. Null for a seat that is not in the layout.

function seatCardText(
  layout: LayoutDocument,
  seatId: string,
  statuses: StatusInput,
  options?: {
    readonly categoryPrices?: Readonly<Record<string, string>>;
    readonly selected?: boolean;
  },
): string | null;

Parameters

ParameterTypeDescription
layoutLayoutDocument
seatIdstring
statusesStatusInput
optionsoptional{ readonly categoryPrices?: Readonly<Record<string, string>>; readonly selected?: boolean; }

Default {}

Returns

string | null

@openseat/react/src/booking-view.ts

function

sectionBandPolygons

The split band of a mixed-price section in the overview: a strip along the bottom of its outline (in its own frame), divided between its ticket types in proportion to their seats, each in its category colour. Display-only polygons, clipped to the section outline so they never stick out. Empty when fewer than two of the categories have a colour.

function sectionBandPolygons(
  section: Section,
  mix: readonly { readonly categoryId: string; readonly count: number }[],
  categories: readonly Category[],
): Polygon[];

Parameters

ParameterTypeDescription
sectionSection
mixreadonly { readonly categoryId: string; readonly count: number; }[]
categoriesreadonly Category[]

Returns

@openseat/react/src/booking-view.ts

function

tierSwatchBackground

A conic gradient of up to six colours in equal slices ("conic-gradient(#a 0deg 120deg, #b 120deg 240deg, …)").

function tierSwatchBackground(
  colours: readonly string[],
  fallback: string,
): string;

Parameters

ParameterTypeDescription
coloursreadonly string[]
fallbackstring

Returns

string

@openseat/react/src/BookingMap.tsx

function

zoneTierColours

Ticket-type colours of the layout's zones (GA areas and zone sections) and booths, in layout order, the ones still on sale first; the layout's category colours when no zone has one. At most six.

function zoneTierColours(
  layout: LayoutDocument,
  statuses?: StatusInput,
): string[];

Parameters

ParameterTypeDescription
layoutLayoutDocument
statusesoptionalStatusInput

Returns

string[]

@openseat/react/src/BookingMap.tsx

Interfaces16

interface

AreaQuantityPickerProps

interface AreaQuantityPickerProps

The props of AreaQuantityPicker: every member is listed there.

@openseat/react/src/AreaQuantityPicker.tsx

interface

BookingExtraItem

A non-seat line of the basket, as the buyer reads it.

interface BookingExtraItem

Members

MemberTypeDescription
labelstring

What the buyer sees, for example a cart line label ("4 × Fan Pit", "Booth B8"). Never an ID.

quantitynumber

Tickets on this line.

Every member is readonly.

@openseat/react/src/BookingMap.tsx

interface

BookingMapHandle

interface BookingMapHandle

Members

MemberTypeDescription
mapSeatMapHandle | null
focusmethod() => void

Moves keyboard focus into the map.

zoomToSectionmethod(sectionId: string) => void

Zooms to a section, as a click on it in the overview does.

showOverviewmethod() => void

Zooms back out to the whole venue.

Every member is readonly.

@openseat/react/src/BookingMap.tsx

interface

BookingMapProps

The props of BookingMap. Only layout is required; pass statuses from useInventory for live availability.

interface BookingMapProps

The props of BookingMap: every member is listed there.

@openseat/react/src/BookingMap.tsx

interface

SeatLegendProps

interface SeatLegendProps

The props of SeatLegend: every member is listed there.

@openseat/react/src/BookingMap.tsx

interface

SeatMapHandle

interface SeatMapHandle

Members

MemberTypeDescription
rendererSeatMapRenderer | null

The underlying renderer, or null before mount and after unmount.

fitToScreenmethod() => void
zoomInmethod() => void
zoomOutmethod() => void
zoomBymethod(factor: number) => void
isGestureActivemethod() => boolean

True while an edit gesture (drag, marquee, transform, handle drag) is in progress.

cancelGesturemethod() => void

Aborts the gesture in progress and restores what was drawn before it, without a commit event.

Every member is readonly.

@openseat/react/src/seat-map-core.tsx

interface

SeatMapProps

interface SeatMapProps extends RendererEvents

Extends RendererEvents

The props of SeatMap: every member is listed there.

@openseat/react/src/seat-map-core.tsx

interface

SectionZoomOptions

interface SectionZoomOptions

Members

MemberTypeDescription
overviewScaleoptionalreadonlynumber

Viewport scale below which the map is in the overview. Default: the fit-to-screen scale × 1.6.

@openseat/react/src/BookingMap.tsx

interface

SelectionSummaryProps

interface SelectionSummaryProps

The props of SelectionSummary: every member is listed there.

@openseat/react/src/BookingMap.tsx

interface

UseHoldOptions

Options for useHold. Read when the controller is created (when the adapter or event changes).

interface UseHoldOptions

Members

MemberTypeDescription
maxTicketsoptionalnumber

Per-buyer ticket limit checked before asking the server (seats, GA places and booths count one each).

layoutoptionalLayoutDocument

The layout, so a whole table counts its seats towards maxTickets.

Every member is readonly.

@openseat/react/src/hooks.ts

interface

UseHoldResult

interface UseHoldResult

Members

MemberTypeDescription
stateHoldState
canHoldboolean

True when hold() may start a booking now: nothing is held or in flight (idle, or after a confirmed booking, a release, an expiry or a failure). After a confirm this is true at once, so a second booking needs no reset.

remainingMsnumber

Milliseconds left on the hold; updates every second while held.

holdmethod(seatIds: readonly string[], areas?: readonly AreaQuantity[]) => Promise<HoldState>

Holds seats and, optionally, area quantities (GA places, booths, whole tables), all or nothing.

confirmmethod(paymentReference: string) => Promise<HoldState>
releasemethod() => Promise<HoldState>
resetmethod() => HoldState

Back to idle after a booking, release, expiry or failure, ready for the next booking.

controllerHoldController

Every member is readonly.

@openseat/react/src/hooks.ts

interface

UseInventoryResult

What useInventory returns: statuses is ready to pass to <BookingMap statuses> and is kept fresh by the adapter's real-time subscription; loading and error describe the last fetch, and refresh() refetches on demand.

interface UseInventoryResult

Members

MemberTypeDescription
statusesReadonlyMap<string, SeatStatusRecord>
loadingboolean
errorBookingError | null
refreshmethod() => Promise<void>

Refetches everything, replacing the cached picture.

Every member is readonly.

@openseat/react/src/hooks.ts

interface

UseLayoutResult

interface UseLayoutResult

Members

MemberTypeDescription
layoutLayoutDocument | null
errorsreadonly LayoutIssue[]
loadingboolean

Every member is readonly.

@openseat/react/src/hooks.ts

interface

UseSeatMapResult

interface UseSeatMapResult

Members

MemberTypeDescription
refRefObject<SeatMapHandle | null>

Pass to <SeatMap ref={...} />.

fitToScreenmethod() => void
zoomInmethod() => void
zoomOutmethod() => void
getRenderermethod() => SeatMapRenderer | null

Every member is readonly.

@openseat/react/src/index.ts

interface

UseSeatSelectionOptions

interface UseSeatSelectionOptions

Members

MemberTypeDescription
layoutLayoutDocument
statusesoptionalStatusesInput
rulesoptionalSelectionRules
initialoptionalreadonly string[]
onUnavailableoptional(seatIds: readonly string[]) => void

Called with seats removed because their status changed to unavailable.

Every member is readonly.

@openseat/react/src/hooks.ts

interface

UseSeatSelectionResult

interface UseSeatSelectionResult

Members

MemberTypeDescription
selectionreadonly string[]
togglemethod(seatId: string) => ToggleResult

Applies a click. Returns why it was refused, if it was.

removemethod(seatId: string) => void
clearmethod() => void
validationSelectionValidation

Full validation for checkout: minimum, availability, categories and orphans.

Every member is readonly.

@openseat/react/src/hooks.ts

Types4

type

LayoutSource

type LayoutSource = LayoutDocument | string | (() => Promise<unknown>);

@openseat/react/src/hooks.ts

type

SeatStatesInput

Seat states keyed by public seat ID, as a Map or a plain object (convenient for JSON from an API).

type SeatStatesInput =
  | ReadonlyMap<string, SeatVisualState>
  | Readonly<Record<string, SeatVisualState>>;

Variants · 2

DiscriminantTypeDescription
ReadonlyMap<string, SeatVisualState>
Readonly<Record<string, SeatVisualState>>

@openseat/react/src/seat-states.ts

type

SelectionChange

type SelectionChange =
  | {
      readonly type: "added";
      readonly seatId: string;
    }
  | {
      readonly type: "removed";
      readonly seatId: string;
    }
  | {
      readonly type: "unavailable";
      readonly seatIds: readonly string[];
    };

Variants · 3

DiscriminantTypeDescription
type: "added"{ readonly type: "added"; readonly seatId: string; }
type: "removed"{ readonly type: "removed"; readonly seatId: string; }
type: "unavailable"{ readonly type: "unavailable"; readonly seatIds: readonly string[]; }

Seats dropped because the host reported them unavailable after they were selected.

@openseat/react/src/BookingMap.tsx

Constants5

const

FEW_LEFT

Below this many places left the picker says "N left".

const FEW_LEFT = 20

@openseat/react/src/AreaQuantityPicker.tsx

const

MAX_LEGEND_TIER_COLOURS

Most tier colours mixed into the zones legend's "Available" swatch.

const MAX_LEGEND_TIER_COLOURS = 6

@openseat/react/src/BookingMap.tsx

const

QUANTITY_SHEET_MAX_WIDTH

At or below this viewport width (CSS pixels) the picker is a bottom sheet.

const QUANTITY_SHEET_MAX_WIDTH = 640

@openseat/react/src/AreaQuantityPicker.tsx

const

SECTION_ZOOM_AUTO_SEATS

sectionZoom: "auto" turns the overview on only for layouts with more seats than this.

const SECTION_ZOOM_AUTO_SEATS = 2500

@openseat/react/src/booking-view.ts

const

TABLE_PRICE_SUFFIX

A category price ending in this is a whole table's price ("₹15,000 a table"), not a seat's.

const TABLE_PRICE_SUFFIX = " a table"

@openseat/react/src/booking-view.ts

Re-exports1

interface

BookableArea

Re-exported from @openseat/booking: Something sold as one unit rather than seat by seat: a zone (a GA area, or a section shown as a zone with showSeats: false, both sold by quantity up to capacity), a booth with an inventory key, or a table marked bookAsWhole. The SDK only reports clicks on these; the host shows its own quantity picker and owns the inventory.