|
| 1 | +# SeatLayer for React Native |
| 2 | + |
| 3 | +[](https://www.npmjs.com/package/@seatlayer/react-native) |
| 4 | +[](https://reactnative.dev/) |
| 5 | +[](https://expo.dev/) |
| 6 | +[](LICENSE) |
| 7 | + |
| 8 | +The official React Native SDK for embedding interactive SeatLayer |
| 9 | +reserved-seating maps in iOS and Android apps. It provides typed selection, |
| 10 | +holds, best available, general admission, multi-floor controls, errors and |
| 11 | +events over a versioned WebView bridge. |
| 12 | + |
| 13 | +[Developer docs](https://docs.seatlayer.io/buyer-sdk/mobile/) · |
| 14 | +[Live demo](https://app.seatlayer.io/demo/play) · |
| 15 | +[Website](https://seatlayer.io/developers/) · |
| 16 | +[Web SDK](https://github.com/seatlayer/seatlayer-sdk) · |
| 17 | +[Flutter SDK](https://pub.dev/packages/seatlayer) · |
| 18 | +[iOS SDK](https://github.com/seatlayer/seatlayer-ios) · |
| 19 | +[AI Toolkit](https://github.com/seatlayer/seatlayer-ai-toolkit) |
| 20 | + |
| 21 | +> **Public preview:** Validate `0.1.x` using a SeatLayer test event and physical |
| 22 | +> iOS and Android devices before production rollout. |
| 23 | +
|
| 24 | +## Install |
| 25 | + |
| 26 | +### Expo |
| 27 | + |
| 28 | +```bash |
| 29 | +npm install @seatlayer/react-native |
| 30 | +npx expo install react-native-webview |
| 31 | +``` |
| 32 | + |
| 33 | +No custom native SeatLayer module is used, so this SDK works with Expo Go when |
| 34 | +the installed Expo SDK includes `react-native-webview`. |
| 35 | + |
| 36 | +### React Native Community CLI |
| 37 | + |
| 38 | +```bash |
| 39 | +npm install @seatlayer/react-native react-native-webview |
| 40 | +npx pod-install |
| 41 | +``` |
| 42 | + |
| 43 | +React Native autolinks `react-native-webview` on Android and iOS. |
| 44 | + |
| 45 | +## Quick start |
| 46 | + |
| 47 | +Give the map a definite height or a full-screen parent. Keep the configuration |
| 48 | +object stable so React rerenders do not reload the chart. |
| 49 | + |
| 50 | +```tsx |
| 51 | +import React, { useEffect, useMemo } from 'react'; |
| 52 | +import { View } from 'react-native'; |
| 53 | +import { |
| 54 | + SeatLayerError, |
| 55 | + SeatLayerView, |
| 56 | + useSeatLayerController, |
| 57 | +} from '@seatlayer/react-native'; |
| 58 | + |
| 59 | +export function SeatMapScreen() { |
| 60 | + const controller = useSeatLayerController(); |
| 61 | + const configuration = useMemo( |
| 62 | + () => ({ |
| 63 | + event: 'ev_your_event_key', |
| 64 | + currency: 'USD', |
| 65 | + maxSelection: 8, |
| 66 | + }), |
| 67 | + [], |
| 68 | + ); |
| 69 | + |
| 70 | + useEffect( |
| 71 | + () => |
| 72 | + controller.on('selectionChanged', (seats) => { |
| 73 | + console.log('Selected seats', seats); |
| 74 | + }), |
| 75 | + [controller], |
| 76 | + ); |
| 77 | + |
| 78 | + return ( |
| 79 | + <View style={{ flex: 1 }}> |
| 80 | + <SeatLayerView |
| 81 | + style={{ flex: 1 }} |
| 82 | + controller={controller} |
| 83 | + configuration={configuration} |
| 84 | + onReady={(info) => { |
| 85 | + console.log( |
| 86 | + `SeatLayer ready: protocol=${info.protocolRevision} mode=${info.mode}`, |
| 87 | + ); |
| 88 | + }} |
| 89 | + onLoadError={(error) => { |
| 90 | + console.error(error.code, error.message); |
| 91 | + }} |
| 92 | + /> |
| 93 | + </View> |
| 94 | + ); |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +Drive checkout-related actions through the controller: |
| 99 | + |
| 100 | +```tsx |
| 101 | +try { |
| 102 | + const hold = await controller.bestAvailable(4); |
| 103 | + if (hold) { |
| 104 | + // Send only the hold id to your trusted backend. |
| 105 | + await beginCheckoutOnServer(hold.holdId); |
| 106 | + } |
| 107 | +} catch (error) { |
| 108 | + if (error instanceof SeatLayerError) { |
| 109 | + showInventoryMessage(error.code, error.message); |
| 110 | + } |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +## Commands |
| 115 | + |
| 116 | +`hold` · `resumeHold` · `extendHold` · `release` · `releaseLabels` · |
| 117 | +`bestAvailable` · `holdGA` · `setSeatTier` · `getSelection` · |
| 118 | +`getCurrentHold` · `getGAAreas` · `getFloors` · `setFloor` · |
| 119 | +`setColorblindSafe` · `zoomIn` · `zoomOut` · `zoomToFit` · `destroy` |
| 120 | + |
| 121 | +All asynchronous command failures reject with `SeatLayerError`. Inventory |
| 122 | +outcomes such as `sold_out`, `not_enough_together`, expired holds and conflicts |
| 123 | +remain distinct codes suitable for buyer-facing recovery. |
| 124 | + |
| 125 | +## Events |
| 126 | + |
| 127 | +Subscribe with `controller.on(name, listener)`. The returned function removes |
| 128 | +the listener. |
| 129 | + |
| 130 | +```tsx |
| 131 | +useEffect(() => { |
| 132 | + const offHold = controller.on('holdChanged', persistOpenHold); |
| 133 | + const offExpired = controller.on('holdExpired', returnBuyerToSelection); |
| 134 | + const offError = controller.on('error', reportSeatLayerError); |
| 135 | + return () => { |
| 136 | + offHold(); |
| 137 | + offExpired(); |
| 138 | + offError(); |
| 139 | + }; |
| 140 | +}, [controller]); |
| 141 | +``` |
| 142 | + |
| 143 | +Events: `ready` · `selectionChanged` · `holdChanged` · `holdRestored` · |
| 144 | +`holdExpired` · `error` · `hint` · `gaClick` · `seatHover` · `deckTap` · |
| 145 | +`checkout` · `unknownEvent` |
| 146 | + |
| 147 | +Unknown future events remain observable through `unknownEvent`; adding a bundle |
| 148 | +event does not crash an older app. |
| 149 | + |
| 150 | +## Security boundary |
| 151 | + |
| 152 | +The app selects and holds inventory. Your trusted backend validates payment, |
| 153 | +inspects the hold and creates the booking. |
| 154 | + |
| 155 | +- Never ship a SeatLayer secret key in JavaScript, the app bundle or WebView. |
| 156 | +- Send only `holdId` and normal checkout context to your backend. |
| 157 | +- Calculate the amount from server-inspected hold items, not device input. |
| 158 | +- Use a stable order id as the booking reference for safe retries. |
| 159 | +- Do not enable arbitrary navigation inside the SDK WebView. |
| 160 | + |
| 161 | +Read [holds and checkout](https://docs.seatlayer.io/buyer-sdk/holds-and-checkout/) |
| 162 | +before connecting a payment flow. |
| 163 | + |
| 164 | +## How the bridge works |
| 165 | + |
| 166 | +The npm package embeds the verified `seatlayer-js@0.30.1` bundle in generated |
| 167 | +inline HTML. This avoids the inconsistent local-file behavior of iOS and Android |
| 168 | +WebViews while keeping the SDK JavaScript independent of a runtime CDN download. |
| 169 | +Chart data and live inventory still come from the configured SeatLayer API. |
| 170 | + |
| 171 | +The protocol guarantees: |
| 172 | + |
| 173 | +- range-negotiated compatibility before rendering; |
| 174 | +- one response per command using correlation ids; |
| 175 | +- native command timeouts and late-reply rejection; |
| 176 | +- monotonic event ordering per event type; and |
| 177 | +- forward-compatible unknown events and fields. |
| 178 | + |
| 179 | +See [the bridge contract](docs/bridge.md) for the wire-level details. |
| 180 | + |
| 181 | +## Layout and lifecycle |
| 182 | + |
| 183 | +- Use a fixed-height or full-screen parent; do not put the map inside a vertical |
| 184 | + `ScrollView`. |
| 185 | +- Keep `configuration` stable with `useMemo`. |
| 186 | +- Change `reloadKey` to deliberately rebuild the WebView. |
| 187 | +- `useSeatLayerController` disposes the controller automatically. |
| 188 | +- Persist an open `holdId` and call `resumeHold` after app restoration. |
| 189 | + |
| 190 | +## Development |
| 191 | + |
| 192 | +```bash |
| 193 | +pnpm install |
| 194 | +pnpm validate |
| 195 | +cd example && pnpm install && pnpm start |
| 196 | +``` |
| 197 | + |
| 198 | +`pnpm validate` regenerates the embedded document, type-checks, runs protocol |
| 199 | +tests, builds ESM/CommonJS/types, and validates the npm tarball. |
| 200 | + |
| 201 | +## Related resources |
| 202 | + |
| 203 | +- [React Native mobile guide](https://docs.seatlayer.io/buyer-sdk/mobile/) |
| 204 | +- [Buyer SDK installation](https://docs.seatlayer.io/buyer-sdk/install/) |
| 205 | +- [Complete checkout example](https://docs.seatlayer.io/examples/complete-checkout/) |
| 206 | +- [Agent-readable documentation](https://docs.seatlayer.io/llms.txt) |
| 207 | +- [SeatLayer GitHub organization](https://github.com/seatlayer) |
| 208 | + |
| 209 | +## License |
| 210 | + |
| 211 | +MIT © SeatLayer |
0 commit comments