About this animation
A waitlist pass with animated referral-to-position updates, with configurable initial position, copy, accent color, and fonts.
Why this one
A queue position usually lives in a sentence. This component makes it a collectible pass with a holographic edge, a fixed dot code, and a visible queue. Each referral moves your glowing marker past six people while the position rolls down by 250. The dot code is decorative, not a QR code.
How it works
onLayoutmeasures the container. Product, pass, queue, and controls occupy proportional screen regions.- A shared referral progress drives the rolling position and your marker's arc. Each digit changes only at its carry boundary and fades by its distance from the resting line.
- On an invitation, a 540 ms avatar flight accompanies a spring pass lift; the number settles over 900 ms. Six neighboring dots move back with springs delayed by 20 ms each.
- Exclusive Gesture Handler pan and tap gestures tilt the pass up to eight degrees. The holographic sweep rotates inside a clipped five-point strip, and release springs back with gesture velocity.
BlurTargetViewsupplies the background for the 50-intensity glass pass on Android. Radial gradients provide ambient light without covering text.- Clipboard work belongs to the host's async callback. Only successful completion draws the check and shows “Link copied” for 1.6 seconds.
Install
npx expo install @shopify/react-native-skia@2.14.0 expo-blur@~58.0.5 expo-linear-gradient@~58.0.3 react-native-gesture-handler@~3.3.0 react-native-reanimated@4.7.1 react-native-worklets@0.13.0Copy this folder without demo/. Wrap your app in GestureHandlerRootView, configure Reanimated as required by your Expo version, and load any custom fonts before rendering. For Skia on web, serve the matching CanvasKit WASM using Skia's Expo web setup. The camera image and brand artwork are demo-only; expo-image is only needed by the included demo.
Usage
import { WaitlistPositionPass } from './waitlist-position-pass';
export function WaitlistScreen() {
return <WaitlistPositionPass />;
}import { useRef } from 'react';
import {
WaitlistPositionPass,
type WaitlistPositionPassRef,
} from './waitlist-position-pass';
export function EarlyAccessScreen() {
const pass = useRef<WaitlistPositionPassRef>(null);
return (
<WaitlistPositionPass
ref={pass}
title="Studio camera · Early access"
initialPosition={2481}
spotsPerInvite={250}
accentColor="#38BDF8"
fontFamily="YourUIFont"
monoFontFamily="YourMonoFont"
onCopyLink={async () => {
// Connect your installed clipboard provider here.
// Reject or return false if the copy fails.
}}
/>
);
}ref.invite() uses the same interaction as the button. ref.reset() fades out, restores the initial position, and fades in. The demo calls invite at 1 and 4 seconds, resets at 6.7 seconds, and repeats every 7.6 seconds. Its clipboard feedback is a simulation because this project has no installed clipboard module. A production host must supply onCopyLink and call invite() after its referral service confirms a friend joined. This component is a visual interaction, not a referral backend.
Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
| initialPosition | number | 2481 | Initial queue rank, use a positive integer |
| total | number | 18204 | Queue population |
| spotsPerInvite | number | 250 | Rank improvement per referral |
| maxInvites | number | 3 | Referral limit, capped at three for the 40-person visualization |
| title | string | Early access | Pass heading |
| inviteEstimate | string | Est. invite: Nov 18 | Invitation estimate |
| product | ReactNode | — | Product content above the pass |
| brandTile | ReactNode | — | Host brand artwork |
| initials | string[] | AL, MJ, SK | Friend initials |
| fontFamily | string | system | Loaded UI font |
| monoFontFamily | string | system | Loaded numeral font |
| accentColor | string | #38BDF8 | Accent; use a six-digit hex color |
| colorScheme | light / dark / auto | dark | Appearance |
| onPositionChange | (position, invited) => void | — | State change callback |
| onCopyLink | () => Promise<void | boolean> | — | Clipboard adapter; omitted means preview simulation |
| style | StyleProp<ViewStyle> | — | Root layout override |
| testID | string | — | Root testing identifier |
Compatibility
Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Gesture Handler 3.3, and Skia 2.14.0. Uses Expo Go-compatible installed modules. Android 12+ uses the optimized blur method; older Android falls back to a translucent surface. This piece is intentionally unregistered, so those app bundles do not mount it. Supports iOS, Android, and web.
Accessibility
Buttons expose roles and labels, with minimum 52-point hit areas. The pass announces the current rank and supports tapping or the accessibility activation action as an alternative to dragging. Referral feedback has a polite live region. Reduced motion removes mesh drift, tilt, flight, marker arcs, and spring displacement; the number updates with a short fade. All animation runs on the UI thread; React updates only for discrete state changes. Timers and ambient animation are cleaned up on unmount.
Icons: Lucide (ISC), copied from the project kit. The fictional Orrin brand and local camera artwork appear only in the demo.
Customization notes
The countdown follows its time input; position changes use interruptible springs.




