Skip to content
Native Animation
Gift Wrap Toggle animation preview

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

npx nativeanimation add gift-wrap-toggle
Skia

Gift Wrap Toggle

A gift-wrap toggle with folding paper and a tied ribbon, with configurable product image, product copy, fonts, and accent color.

$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 gift-wrap toggle with folding paper and a tied ribbon, with configurable product image, product copy, fonts, and accent color.

Why this one

A checkbox sells a service. This control shows the gift taking shape. The tied bow and live handwritten tag make the upgrade feel personal before checkout.

How it works

  1. onLayout measures the available product stage. Skia draws the package in a normalized 300-point coordinate system.
  2. One interruptible shared progress drives the side folds, perspective skew, fold shadows, descending lid, ribbon strokes, bow loops, and one small settling bounce.
  3. The normalized timeline is side folds 0–0.43, lid 0.35–0.58, cross ribbon 0.53–0.76, tied bow 0.74–0.94, and settling 0.90–1. The default spring has a 1300 ms duration; the duration prop is capped at 1400 ms.
  4. A horizontal pan controls the same progress directly. Release projects velocity toward the wrapped or unwrapped state; tapping the product or switch also changes state. Reversing unwraps in the opposite order.
  5. Paper changes reveal the next patterned material through a diagonal clip. Message edits update React only when text changes; shared values reveal the hanging tag.
  6. The demo owns its timers, wraps after 500 ms, changes papers, types a birthday message, and unwraps. Any touch or input focus stops the loop.

Install

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

Wrap your app in GestureHandlerRootView. For web, initialize CanvasKit before importing this component using Skia's WithSkiaWeb or your existing lazy Skia loader. The demo expects the host to load its fonts.

Usage

import GiftWrapToggle from './gift-wrap-toggle';

export function GiftOptions() {
  return (
    <GiftWrapToggle
      source={require('./assets/mug.png')}
      productName="Ceramic mug"
      onWrappedChange={(wrapped) => updateOrder({ giftWrap: wrapped })}
    />
  );
}

Customize the presentation and control it externally:

const giftRef = useRef<GiftWrapToggleRef>(null);

<GiftWrapToggle
  ref={giftRef}
  source={productImage}
  accentColor="#9E2345"
  duration={1200}
  colorScheme="light"
  fontFamily="YourInterfaceFont"
  titleFontFamily="YourDisplayFont"
  noteFontFamily="YourItalicFont"
  onPaperChange={savePaper}
  onMessageChange={saveMessage}
/>;

// Invoke from an event handler.
giftRef.current?.setWrapped(true);
giftRef.current?.setPaper('dots');
giftRef.current?.setMessage('Happy birthday!');

The ref exposes setWrapped(boolean), setPaper('stripe' | 'dots' | 'kraft'), setMessage(string), and reset().

Props

PropTypeDefaultDescription
sourceImageProps['source']—Product image; local assets are recommended.
productNamestringCeramic mugProduct title.
productDetailstringHand-finished stonewareProduct subtitle.
pricenumber28Product price in dollars.
wrapPricenumber4Gift wrap surcharge in dollars.
accentColorstring#BE123CRibbon and active control color.
durationnumber1300Spring timeline duration, clamped to 300–1400 ms.
fontFamilystringSystemInterface font.
titleFontFamilystringSystemProduct title font.
noteFontFamilystringSystemGift tag font.
colorScheme'light' | 'dark' | 'auto'autoColor treatment.
accessibilityLabelstringGift wrapSwitch label.
onWrappedChange(wrapped: boolean) => void—Requested wrap state callback.
onPaperChange(paper: Paper) => void—Paper selection callback.
onMessageChange(message: string) => void—Message callback, capped at 120 characters.
onInteraction() => void—Touch or input focus callback.
styleStyleProp<ViewStyle>—Root container style.
testIDstring—Root test identifier.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Skia 2.14.0.

Accessibility

The switch exposes its checked state; paper controls are labeled radios with selection state. The product has an accessible activation action as an alternative to swiping. Switch and paper changes announce their requested state, and the order total and gift status use polite live regions. The message field has a label and a 120-character limit. All touch controls are at least 44 points tall. Reduced motion replaces wrapping and paper changes with 200 ms fades and removes perspective, bounce, bow swing, and press scaling. No rolling numbers, particles, or blur are used.

Icons: Lucide (ISC). The demo uses the Ember & Oat brand tile and supplied local product artwork from the project kit.