Skip to content
Native Animation
Split-Flap Board animation preview

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

npx nativeanimation add split-flap-board

Split-Flap Board

A departure board with cascading split-flap updates and configurable departures, copy, board 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

Related guides

A departure board with cascading split-flap updates and configurable departures, copy, board color, and fonts.

How it works

  1. The component measures its own container and derives the screen spacing, board height, row height, cell type, and title scale from that size.
  2. Two data buffers hold the visible and incoming board states. One continuous Reanimated shared value moves between those buffers, so an update can retarget while a previous flip is still running.
  3. Every character is a precomputed vertical strip driven by that same UI-thread progress value; no React state or per-step JavaScript timer is involved. A cell advances through no more than six intermediate characters, each flap lasts 55–70 ms, and positions start 18 ms apart from left to right. Distant letters skip directly to the destination after the sixth intermediate character, so the complete row always settles in 1.1 seconds or less.
  4. Each strip rolls and folds with rotateX plus a light motion blur around the hinge. The surrounding strips remain visible, so every column keeps showing a complete value while individual positions move, and every effect resolves to zero at rest.
  5. Tapping a row selects it and advances its status through a small state machine. The forwarded ref can update all departures or only cycle destinations for autoplay and external control.
  6. BOARDING receives the single amber accent, while a matte panel, inset separators, two-layer shadow, and subtle rivets provide depth without imagery.

Install

npx expo install expo-linear-gradient react-native-reanimated react-native-worklets

Usage

import SplitFlapBoard from './SplitFlapBoard';

export function TripScreen() {
  return (
    <SplitFlapBoard
      departures={[
        { id: 'portland', time: '8:40 AM', destination: 'Portland', gate: 'B12', status: 'ON TIME' },
        { id: 'austin', time: '9:15 AM', destination: 'Austin', gate: 'A07', status: 'BOARDING' },
      ]}
    />
  );
}

Drive the board from live data and tune its personality:

import { useRef } from 'react';
import SplitFlapBoard, { type SplitFlapBoardRef } from './SplitFlapBoard';

export function LiveTripScreen() {
  const boardRef = useRef<SplitFlapBoardRef>(null);

  return (
    <SplitFlapBoard
      ref={boardRef}
      departures={departures}
      accentColor="#FFB84D"
      backgroundColor="#0B0B10"
      boardColor="#111216"
      flipDuration={64}
      monoFontFamily="YourMonoFont"
      titleFontFamily="YourDisplayFont"
      onRowPress={(departure) => openDeparture(departure.id)}
      onChangeEnd={(nextDepartures) => trackBoardUpdate(nextDepartures)}
    />
  );
}

The forwarded ref exposes update(nextDepartures), cycleDestinations(destinations), selectRow(index), and reset().

Why this one

Most split-flap recreations stop at a novelty text effect. This version places the motion inside a credible trip product screen, keeps every cell on one interruptible UI-thread timeline, and adds two material cues that make the mechanism legible: a shadow that gathers on the falling half and a shadow that clears from the incoming lower half. Stable row layers prevent character changes from remounting mid-flip, while exact column timing, live row interaction, realistic status variety, responsive composition, and reduced-motion behavior make it useful beyond a looping demo.

Props

PropTypeDefaultDescription
departuresDeparture[]Four generic departuresInitial board rows; the first four are displayed.
eyebrowstring'TRIP 04 / DEPARTURES'Small product-screen label above the title.
titlestring'Your trip'Main screen heading.
subtitlestring'Four departures · Terminal A'Supporting trip summary.
boardLabelstring'LIVE DEPARTURES'Small heading inside the board.
accentColorstring'#FFB84D'Active-row, live-indicator, and boarding accent.
backgroundColorstring'#0B0B10'Outer screen background.
boardColorstring'#111216'Matte board surface.
flipDurationnumber64Duration of each character flap in milliseconds, clamped to 55–70 ms; cells begin 18 ms apart.
fontFamilystringSystem fontScreen-copy and label font.
titleFontFamilystringSystem fontMain title font.
monoFontFamilystringSystem fontDeparture-cell font.
onRowPress(departure, index) => void—Called when a row is selected and its next status is chosen.
onChangeEnd(departures) => void—Called after an animated update settles.
styleStyleProp<ViewStyle>—Outer-container style.
testIDstring—Test identifier for the outer container.

Compatibility

  • Expo SDK 58
  • React Native 0.88
  • Reanimated 4.7 and Worklets 0.13
  • iOS: supported
  • Android: supported
  • Web: supported
  • Works in Expo Go because its native dependencies are included in Expo SDK 58

Accessibility

Each departure is a labeled button with its time, destination, gate, and status, plus selected state and a clear action hint. The entire row is at least 57 points tall. With reduced motion enabled, updates replace cell content immediately without rotateX, cascade, shadow, or highlight motion; tapping remains available as the complete alternative to external ref control.