Skip to content
Native Animation
Seat Map Picker animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add seat-map-picker
SkiaGlass

Seat Map Picker

An airplane seat picker with radial fills and a reclining upgrade preview, with configurable seats, pricing, accent color, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

An airplane seat picker with radial fills and a reclining upgrade preview, with configurable seats, pricing, accent color, and fonts.

Why this one

The airplane has a nose, cockpit, windows, exit doors, and wings rather than a generic grid. Selecting a seat reveals its color through an actual circular Skia clip; an upgrade zooms toward the selected row and demonstrates the additional room with a reclining seat drawn in profile. The previous choice remains selected until the upgrade is accepted.

How it works

  1. onLayout sizes a 90%-width fuselage with four 2–2 Comfort+ rows and twenty-four 3–3 economy rows. Visible seats measure 44 and 32 points respectively, with 44-point touch targets.
  2. A composed pan/tap gesture drives a continuous UI-thread scroll position. Wings move at 92% of the cabin's scroll speed. Buttons offer equivalent navigation without dragging.
  3. A single selection reveal value drives the new seat's clipped circular fill and the previous seat's clearing. Touch pressure uses a separate spring so release cannot cancel selection.
  4. Premium selection opens a dismissible preview, dims other seats, and derives row zoom from preview progress. Skia rotates the seat back from 0 to 30 degrees in a slow loop.
  5. Accepting the upgrade commits the seat and adds a soft purple halo. Prices use a shared numeric value, digit carry, and distance fades; digits settle on whole numbers.
  6. React updates only for layout and selection/preview/confirmation events. The demo controls a cancellable 7-second timeline through the public ref.

Install

npx expo install @shopify/react-native-skia expo-blur expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-worklets

Wrap your app in GestureHandlerRootView. Follow the installed Skia version's web setup and load CanvasKit before importing this module on web. In this catalog, the host must register this demo through its existing withSkia loader. Fonts are optional; the component uses system fonts unless supplied. The demo's Geist fonts are loaded by the catalog host.

Usage

import SeatMapPicker from './seat-map-picker';

export function SeatScreen() {
  return <SeatMapPicker onConfirm={(seat) => saveSeat(seat.id)} />;
}

With inventory and personality knobs:

import { useRef } from 'react';
import { SeatMapPicker, type SeatMapPickerRef } from './seat-map-picker';

export function FlightSeats() {
  const picker = useRef<SeatMapPickerRef>(null);
  return (
    <SeatMapPicker
      ref={picker}
      flightLabel="Your upcoming flight"
      seats={[{ id: '12B', row: 12, letter: 'B', occupied: true }]}
      accentColor="#0EA5E9"
      premiumColor="#C084FC"
      seatPrice={24}
      upgradePrice={89}
      legroomInches={5}
      zoomScale={1.6}
      springDamping={28}
      colorScheme="light"
      onConfirm={(seat) => saveSeat(seat.id)}
    />
  );
}

saveSeat represents your application's persistence callback. Give the component a bounded parent; its root uses flex: 1.

Props

PropTypeDefaultDescription
seatsSeatMapSeat[][]Occupancy by ID in the fixed 28-row layout; omitted IDs are available.
brandReactNode—Optional leading brand tile and wordmark.
flightLabelstringYour flightFlight metadata.
titlestringChoose your seatMain heading.
accentColorstring#0EA5E9Economy selection fill.
premiumColorstring#C084FCPremium selection, seat preview, and halo.
seatPricenumber24Economy seat fee in dollars.
upgradePricenumber89Premium seat fee in dollars.
legroomInchesnumber5Additional legroom shown in the preview.
zoomScalenumber1.6Preview zoom, clamped to 1–2.
springDampingnumber28Spring damping, with a minimum of 28.
fontFamilystringSystemBody and metadata typeface.
mediumFontFamilystringfontFamilyHeading and button typeface.
monoFontFamilystringSystemPrice and row-number typeface.
colorScheme'light' | 'dark' | 'auto'autoAppearance.
onSeatChange(seat: SeatSelection) => void—Selection committed.
onConfirm(seat: SeatSelection) => void—Confirmation pressed.
onPreview(seat: SeatSelection) => void—Premium preview opened.
onInteraction() => void—User touches the map or a control.
accessibilityLabelstringAirplane seat mapMap label.
styleStyleProp<ViewStyle>—Root styling.
testIDstring—Root test identifier.

SeatSelection includes id, location, premium, and price. Comfort+ rows use A–D; economy rows use A–F.

Ref methods

  • selectSeat(id): select economy or open a premium preview; occupied and unknown IDs are ignored.
  • scrollToRow(row): scroll to a row, clamped to 1–28.
  • upgrade(): commit the previewed seat.
  • dismissPreview(): preserve the previous selection and close the preview.
  • reset(): clear the selection and return to the nose.

Demo timeline

After an 80 ms start delay: scroll to row 14 at 120 ms, select 14A at 650 ms, return to the nose at 1,100 ms, preview 3C at 1,730 ms, accept the upgrade at 4,050 ms, and restart at 7,000 ms. The preview opens before 3 seconds. Any user touch clears all remaining timers and stops the loop until playback is re-enabled or the demo remounts.

Compatibility

Designed for Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, and Skia 2.14.0. Uses installed Expo Go modules; a custom native host must configure these dependencies. Android blur uses a BlurTargetView with the SDK 31+ blur method and a translucent fallback on older Android versions.

Supports iOS, Android, and web. The production catalog must load Skia lazily; this folder does not modify the catalog registry.

Accessibility

All seats expose a button role, row/position/class/price label, occupied state, selection state, and activation action. Occupied seats cannot be selected. Upgrade opening, selection, dismissal, and confirmation announce state changes; selection details also use a polite live region. All buttons and seat hit regions are at least 44 points. The preview identifies itself as modal to assistive technology.

Reduced motion removes zoom, wing parallax, reclining motion, press movement, radial expansion, and glass blur. Selection/preview changes fade over 200 ms; prices switch directly without rolling. Scroll jumps directly to its destination. Up/down buttons are the tap alternative to dragging.

Icons: Lucide (ISC), copied from the repository kit. The Northwind tile and gradient follow the kit's demo brand definition. No image assets or remote requests are needed.

Layout

At 320×694 and 390×844, controls sit inside the bottom sheet, the upper-right viewer close area stays free, and the preview begins around 1.8 seconds.