Skip to content
Native Animation
Loyalty Stamp Card animation preview

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

npx nativeanimation add loyalty-stamp-card
Skia

Loyalty Stamp Card

A loyalty card with physical stamping and a reward-reveal flip, with configurable reward copy, labels, paper 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

A loyalty card with physical stamping and a reward-reveal flip, with configurable reward copy, labels, paper color, and fonts.

How it works

  1. onLayout measures the host width. The card is centered, capped at 420 pt wide, and uses a height of 0.78 × its width. Allow that much vertical space; the host owns safe-area padding.
  2. The front is warm ivory paper with a restrained edge gradient, procedural grain, an inset light border, and two soft shadows. The demo uses the repository's fictional Ember & Oat wordmark and offers a complete After hours palette.
  3. One progress value drives contact (260 ms) and ink spreading (260 ms by default). A noise mask gives the Lucide flame, circular impression, and small droplets uneven ink coverage. The impression compresses slightly and settles at a different small rotation for each slot; no stamp tool obscures the result.
  4. Contact updates the earned count and onStampChange. The count fades and rolls into place on the same frame. Rapid taps queue stamps; reset invalidates pending callbacks and cancels the active sequence.
  5. Stamp ten reveals the reward with a spring-driven turn (damping: 30, stiffness: 300, mass: 1), a 560 ms highlight inside the card material, and ten restrained flecks. Tap the reward copy to return to the front, or use the separate redemption action.
  6. Autoplay begins with eight stamps, collects visits at 450 and 1,200 ms, and reveals the reward before 3 seconds. It repeats every 5 seconds. The demo uses flow layout, a width cap, and scroll; short containers omit the editorial intro and visit history.

Install

npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets

Use the Reanimated/worklets configuration appropriate to your Expo SDK. Skia on web requires CanvasKit to load before importing the component; use your application's deferred Skia loader. The demo fonts are loaded by the host application, and are not dependencies of the copyable component.

Icons: Lucide (ISC). The flame and scan-line paths are copied from the repository kit; no remote assets are required.

Usage

import { LoyaltyStampCard } from './loyalty-stamp-card';

<LoyaltyStampCard initialStamps={8} style={{ flex: 0, height: 268 }} />;

Pass enough height for the card's aspect ratio. At 343 pt wide, the card is approximately 268 pt tall. The host should scroll when enlarged text or long custom copy needs more space.

import { useRef } from 'react';
import { Button, View } from 'react-native';
import { LoyaltyStampCard, type LoyaltyStampCardRef } from './loyalty-stamp-card';

function Membership() {
  const card = useRef<LoyaltyStampCardRef>(null);
  return (
    <View style={{ flex: 1, padding: 24 }}>
      <LoyaltyStampCard
        ref={card}
        initialStamps={7}
        clubName="NEIGHBORHOOD CLUB"
        accentColor="#9F291D"
        highlightColor="#EEA04B"
        inkDuration={280}
        onStampChange={(count) => saveVisit(count)}
        onRedeem={() => openRedemption()}
      />
      <Button title="Collect stamp" onPress={() => card.current?.stamp()} />
    </View>
  );
}

saveVisit and openRedemption are application callbacks. The component is a visual interaction; it does not scan QR codes or validate rewards.

Props

PropTypeDefaultPurpose
initialStampsnumber0Initial count, clamped to 0–10; use reset for later changes.
brandNamestringCoffee clubPrinted wordmark.
clubNamestringLOYALTY CLUBUppercase membership heading.
countLabelstringVisits collectedPrefix for earned count.
rewardTitlestringYour next drink\nis on us.Two-line reward copy.
rewardCodestringCLUB-0001Redemption code.
redeemLabelstringRedeem in storeReward action label.
accentColorstring#A44F38Ink and dark gradient stop.
highlightColorstring#D89669Warm gradient stop and particles.
paperColorstring#FAF4E8Cream stop of the front surface.
surfaceAccentColorstring#E8D8C1Shadow stop of the paper finish.
surfaceHighlightColorstring#FFFDF6Light stop of the paper finish.
fontFamilystringSystemButton font.
titleFontFamilystringSystemEditorial headings.
monoFontFamilystringSystemMetadata, rolling count, and code font.
inkDurationnumber260Ink spread duration in milliseconds.
onStampChange(count: number) => void—Contact callback on the JS thread.
onComplete() => void—Called after printing stamp ten.
onRedeem() => void—Redemption button callback.
accessibilityLabelstringAdd a loyalty stampFront action label; count is appended.
countSuffixstring/ 10Printed earned-count suffix.
rewardLabelstringREWARD UNLOCKEDReward status heading.
returnLabelstringReturn to your stamp cardReward-copy accessibility label.
formatStampCount(count: number) => stringEnglish earned countFormats the front accessibility announcement.
textColorstring#382B24Front heading and count ink.
mutedColorstring#8B7769Front metadata and empty slot outlines.
rewardTextColorstring#FFF8EEReward heading and code ink.
styleStyleProp<ViewStyle>—Outer container style.
testIDstring—Integration identifier.

Ref methods: stamp(), showFront(), and reset(count = 0). Returning to the front preserves the count. A full card can be tapped to reopen its earned reward. Reset cancels the active sequence and queued taps.

Compatibility

Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, react-native-worklets 0.13.0, and Skia 2.14.0. Uses the installed Expo Go stack. Supports iOS, Android, and web. No server, recording, or preview is included.

Accessibility

All actions have button roles and labels. The front announces earned stamps. The hidden face is removed from the accessibility tree and cannot receive touches. Every interaction is available by tap; the redemption button has a 48 pt minimum height. Reduced motion replaces the physical stamp with a 180 ms ink fade and the 3D flip with a 200 ms crossfade, removes particles, keeps a static material highlight, and keeps count changes stationary.

Stable control IDs append -stamp, -front, and -redeem to testID. All printed copy and accessibility announcements can be customized. Supply the full palette when changing the paper color.

The demo reserves 60–76 pt at the top for the viewer's close area. It offers Daylight and After hours appearances, uses MM/DD visit dates, and provides a 52 pt collect action and 48 pt appearance selectors. Fixed card copy is intended for short membership names; localize or resize long content in the host application.