Saltar al contenido principal

<TicketRow>

One row per ticket type: name + "from $X" price + quantity stepper. Consumes the aggregated view from useTicketTypes() so the displayed price is the cheapest active tier and the stepper clamps against the rolled-up totalAvailable and maxPerOrder. Rendered by <AvailabilityList> for each ticket type the event exposes; mount directly when building a custom picker layout.

Import​

import { TicketRow } from "@feelrift/react";

Basic usage​

import { RiftEvent, RiftProvider, TicketRow } from "@feelrift/react";

<RiftProvider>
<RiftEvent eventId="evt_summerfest_2026">
<TicketRow ticketTypeId="tt_ga" />
<TicketRow ticketTypeId="tt_vip" showRemaining />
</RiftEvent>
</RiftProvider>;

Renders null when the ticket type has no active wave (sold out across all tiers, ID stale, etc.). The picker speaks ticket types only — the wave concept doesn't surface here; the breakdown across waves shows up in <EstimateBreakdown> once a quantity is selected.

Props​

Accepts all HTMLAttributes<HTMLDivElement> (except children — see below) plus:

NameTypeRequiredDefaultDescription
ticketTypeIdstringYes—Ticket-type ID this row represents (tt_…).
asChildbooleanNofalseSwap the wrapping <div> for a consumer-supplied element via Radix Slot.
childrenReactNode | ((args: { ticketType, quantity, setQuantity }) => ReactNode)No—Replace the default inner contents. Function form receives the aggregated ticket-type data + quantity + setter.
showRemainingbooleanNofalseShow a "N left" indicator next to the price when availability is positive. Scarcity-pressure is opt-in.
now() => DateNo—Wall-clock source forwarded to useTicketTypes for active-wave filtering. Tests / Storybook use this to freeze.
classNamestringNo—Appended to the SDK's rift-ticket-row class.
…restHTMLAttributes<HTMLDivElement>No—Forwarded to the root element.

children​

Two forms:

  • JSX child — replaces the default inner row contents (name, price, stepper). The wrapping <div> still applies the SDK's class composition.
  • Function child — receives { ticketType, quantity, setQuantity } and returns whatever you want. Useful for an entirely custom stepper while keeping the SDK's clamping and selection wiring.
<TicketRow ticketTypeId="tt_ga">
{({ ticketType, quantity, setQuantity }) => (
<MyCard
title={ticketType.name}
price={ticketType.fromPrice}
count={quantity}
onChange={setQuantity}
/>
)}
</TicketRow>

showRemaining​

Default false. When true, the row appends a " · N left" indicator to the price when totalAvailable > 0. Scarcity messaging affects conversion both ways — leave it off unless the design explicitly wants it.

asChild​

When true, the row delegates rendering to its immediate child via Radix Slot, preserving the className composition and the default inner contents:

<TicketRow ticketTypeId="tt_ga" asChild>
<article className="my-card" />
</TicketRow>

Render output​

<div class="rift-ticket-row">
<div class="rift-ticket-row__info">
<div class="rift-ticket-row__name">{ticketType.name}</div>
<div class="rift-ticket-row__price">
{From $40.00 / Sold out}
<span class="rift-ticket-row__remaining"> · {N} left</span>
</div>
</div>
<div class="rift-ticket-row__stepper">
<button aria-label="Remove one {name} ticket">−</button>
<span class="rift-ticket-row__quantity">{quantity}</span>
<button aria-label="Add one {name} ticket">+</button>
</div>
</div>

Stepper aria-labels carry the ticket-type name and are localized via useLocale() — multiple rows on the same page produce distinct, AT-readable labels.

Behavior​

  • Cheapest-tier price. The displayed fromPrice comes from the aggregated view's first active wave, sorted cheapest-first. Selecting more than the current tier's available walks the selection into the next-cheapest tier — the per-row stepper doesn't surface that; <EstimateBreakdown> does.
  • Stepper clamping. Upper bound is min(maxPerOrder, totalAvailable). maxPerOrder is the most permissive active cap with available inventory; totalAvailable is the sum. The buttons disable at the bounds.
  • Sold-out state. When totalAvailable === 0, the price slot shows "Sold out" and the stepper buttons disable.
  • Selection context. Reads from / writes to useAvailabilitySelection() — the same context <AvailabilityList> and <ReserveButton> consume. Multiple rows for the same event share a selection.

Examples​

Custom stepper via function children​

<TicketRow ticketTypeId="tt_ga">
{({ ticketType, quantity, setQuantity }) => (
<div className="my-row">
<strong>{ticketType.name}</strong>
<input
type="number"
value={quantity}
min={0}
max={Math.min(ticketType.maxPerOrder, ticketType.totalAvailable)}
onChange={(e) => setQuantity(Number(e.target.value))}
/>
</div>
)}
</TicketRow>

Scarcity indicator on​

<TicketRow ticketTypeId="tt_vip" showRemaining />

Storybook with a frozen clock​

<TicketRow ticketTypeId="tt_ga" now={() => new Date("2026-07-01T12:00:00Z")} />
  • <AvailabilityList> — renders one <TicketRow> per ticket type by default.
  • <EstimateBreakdown> — surfaces the wave-tier breakdown that this row deliberately hides.
  • useTicketTypes — the aggregated view this row consumes. Public types exported from @feelrift/react.
  • Pricing and estimates — the wave-tier model and the cheapest-first aggregation.