---
title: "Native picker and custom layouts"
description: "Use the React Native 0.3.4 picker as a ready-made flow, customise any complete part, or compose a host-owned layout from public components."
---

React Native `0.3.4` ships the complete native picker on npm. It adds a
ready-made screen, controlled modal, generated themes and locale strings,
standalone scoped chrome, adaptive presentation, and capability-aware actions
while preserving the raw-map API.

<Aside type="note" title="Installable release">
  Install `@seatlayer/react-native@0.3.4`; do not pin a repository commit. The
  public APIs on this page are exported by that npm package.
</Aside>

## What is native

React Native components own the header, price/floor/section navigation,
accessibility filters, confirmation, tier and quantity decisions, cart, hold
status, checkout, loading, errors, and controls around immersive views.
SeatLayer owns seat geometry, labels, map gestures, venue 3D, panorama, and
authoritative inventory behavior behind the public SDK API.

That seam gives the host a real React Native hierarchy without duplicating the
venue engine, inventory state, or hold contract.

## Integration ladder

| Level | API | What changes |
| --- | --- | --- |
| Complete flow | `SeatLayerPicker` | Configuration and callbacks only |
| Controlled modal | `SeatLayerPickerModal` | Visibility and dismissal |
| Branded flow | Theme, strings, options, styles, builders | Expression or selected complete parts |
| Custom hierarchy | `SeatLayerPickerScope` + public components | Entire React Native layout |
| Raw map | `SeatLayerView` + `SeatLayerController` | Host owns every buyer state and transition |

The first four levels retain one picker state, ordered inventory actions,
supported-feature checks, hold ownership, lifecycle recovery, and the same
typed checkout handoff.

## Ready-made picker

```tsx
<SeatLayerPicker
  configuration={{
    event: 'ev_your_event_key',
    publicKey: 'pk_test_your_public_key',
    currency: 'USD',
  }}
  themeMode="auto"
  onCheckout={(handoff) =>
    checkoutBackend.begin({ holdId: handoff.holdId })
  }
/>
```

Use `SeatLayerPickerModal` when the picker is controlled by a parent route or
dialog. Both use the same adaptive compact/wide tree and semantic Back ladder.

## Customize without forking

The layers have separate ownership:

| Layer | Use it for | It must not change |
| --- | --- | --- |
| `options` | Behavior, adaptive layout, optional chrome, haptics, languages, display price formatter | Inventory prices, state truth, safe areas |
| `themeMode`, `themeOptions` | Semantic native and map color roles, typography, radii, logos | Authoritative Event branding |
| `locale`, `strings` | One of 37 generated dictionaries and exact host overrides | Event data supplied by SeatLayer |
| `styles` | 62 aesthetic slots for color, type, borders, radii, shadows, opacity | SDK placement, viewport insets, 44-point minimum targets |
| `builders` | Wrap or replace one of 25 complete parts | Inventory state, actions, hold ownership |

```tsx
<SeatLayerPicker
  configuration={configuration}
  themeMode="dark"
  options={{
    layout: 'adaptive',
    chrome: { priceLegend: false },
    haptics: true,
    languages: ['en-GB', 'fr-FR'],
    pricing: {
      formatter: (amount, currency) =>
        new Intl.NumberFormat('fr-FR', {
          style: 'currency',
          currency,
        }).format(amount),
    },
  }}
  locale="fr-FR"
  strings={{ holdAndCheckout: 'Continue' }}
  styles={{
    headerContainer: { backgroundColor: '#172033' },
    continueButton: { backgroundColor: '#5B4B8A' },
  }}
  builders={{
    header: ({ defaultChild }) => defaultChild,
  }}
  onCheckout={continueWithHandoff}
/>
```

The pricing formatter changes display only. Selection, hold totals, and the
trusted server remain authoritative.

## Compose a host-owned layout

`SeatLayerPickerScope` owns one controller and the latest read-only picker
state. Every standalone part reads the same scope, resolved theme,
capabilities, presentation state, and actions.

```tsx
import { View } from 'react-native';
import {
  SeatLayerCartSheet,
  SeatLayerDockBar,
  SeatLayerFloorStrip,
  SeatLayerMapControls,
  SeatLayerPickerAccessibilityFilters,
  SeatLayerPickerChart,
  SeatLayerPickerHeader,
  SeatLayerPickerHoldCountdown,
  SeatLayerPickerScope,
  SeatLayerPickerViewModeControl,
  SeatLayerPriceLegend,
} from '@seatlayer/react-native';

<SeatLayerPickerScope configuration={configuration} themeMode="auto">
  <SeatLayerPickerHeader />
  <SeatLayerPickerHoldCountdown />
  <SeatLayerPriceLegend />
  <SeatLayerFloorStrip />
  <View style={{ flex: 1, position: 'relative' }}>
    <SeatLayerPickerChart style={{ flex: 1 }} />
    <SeatLayerPickerViewModeControl />
    <SeatLayerMapControls
      includeViewModeControl={false}
      showAccessibilityControl={false}
    />
    <SeatLayerPickerAccessibilityFilters />
  </View>
  <SeatLayerDockBar />
  <SeatLayerCartSheet
    expanded={isCartExpanded}
    onExpandedChanged={setCartExpanded}
    onCheckout={continueWithHandoff}
  />
</SeatLayerPickerScope>
```

Call `useSeatLayerPicker()` inside the scope when a host component needs the
latest state, resolved theme, capability availability, presentation state,
or typed actions. Do not create a second chart for the same buyer flow.

## Public component groups

| Buyer job | Public parts |
| --- | --- |
| Identity and navigation | `SeatLayerPickerHeader`, `SeatLayerFloorStrip`, section navigator, `SeatLayerDockBar` |
| Map and filters | `SeatLayerPickerChart`, `SeatLayerMapControls`, view-mode control, price legend, accessibility filters |
| Decisions | Confirmation, tier choice, GA/table prompts, best-available form |
| Cart and holds | Dense ticket lines, cart list/sheet, countdown, hold-lapse notice, undo |
| Immersive views | Venue 3D navigation and seat-view/panorama chrome |
| System states | Loading, retryable/fatal error, empty/sold-out/sales-closed, action error |
| Truth indicators | Test Mode and required bottom-right SeatLayer attribution |

The component catalogue also exports ticket-line grouping utilities so custom
carts can reuse canonical ordering and folding rules rather than reconstructing
them from low-level inventory data.

## State and action guarantees

- One scope owns one controller and one picker state.
- Published state is read-only and represents the complete current picker.
- Stale updates and updates for another event are ignored.
- Inventory-changing actions are serialized.
- Optional controls appear only when the negotiated capability exists.
- Repeated checkout taps join one flight.
- Ordinary picker state omits the opaque hold ID.
- `SeatLayerPickerCheckoutHandoff` crosses that boundary exactly once.
- A rejected host callback rejects that handoff and restores a recoverable state.

Use `useSeatLayerPicker()` actions rather than mutating picker state locally.

## Lifecycle, Back, and recovery

The ready picker observes app state, refreshes availability when supported,
reconciles a lapsed hold, and removes seats that became unavailable while the
app was away. A local countdown is only presentation; the server expiry and
current SDK state are authoritative.

The Back ladder closes the most local decision first: quantity or confirmation,
expanded cart, panorama/3D, focused section, venue, then the host route. Custom
layouts must keep equivalent behavior around the same scoped actions.

## Test Mode and attribution

Test Mode follows authoritative Event state and must remain visible. `0.3.4`
keeps required `Powered by SeatLayer` attribution at the safe bottom-right edge
in compact and wide layouts. Attribution follows the SeatLayer account
configuration and cannot be overridden by client-side styles or builders.

## Production validation

- Pin `@seatlayer/react-native@0.3.4` and `react-native-webview` deliberately.
- Validate Expo Go/development builds and Community CLI paths you support.
- Test compact/wide layout, safe areas, rotation, Back, background/foreground,
  process restoration, hold lapse, checkout rejection, and picker load failure.
- Exercise real tiers, accessibility filters, GA/tables, 3D, panorama, Test
  Mode, and attribution with hosted events.
- Test supported physical iOS and Android devices before rollout.
- Confirm the backend inspects the hold and reuses one booking reference for retries.

## Product scope

The native picker selects one Event. It does not accept a Performance
Group or Season as native configuration.

## Related pages

- [React Native quick start](/buyer-sdk/react-native)
- [Raw-map API](/buyer-sdk/react-native/raw-map)
- [Compare mobile SDKs](/buyer-sdk/mobile)
- [`v0.3.4` source](https://github.com/seatlayer/seatlayer-react-native/tree/v0.3.4)
- [Tagged picker entry point](https://github.com/seatlayer/seatlayer-react-native/blob/v0.3.4/src/picker/SeatLayerPicker.tsx)
- [Tagged scope](https://github.com/seatlayer/seatlayer-react-native/blob/v0.3.4/src/picker/SeatLayerPickerScope.tsx)
- [Tagged example](https://github.com/seatlayer/seatlayer-react-native/tree/v0.3.4/example)
- [Tagged changelog](https://github.com/seatlayer/seatlayer-react-native/blob/v0.3.4/CHANGELOG.md)
- [Troubleshooting matrix](/buyer-sdk/mobile#troubleshooting)