# Stacked Cards: React Native stacked card carousel
> Stacked Cards is a React Native and Expo carousel where a horizontal
> swipe fans the neighbouring cards out into a scaled, tilted 3D stack:
> an Animated.FlatList of invisible spacers owns the snap paging and
> writes its scroll offset into one Reanimated shared value that every
> card interpolates on the UI thread. Sold by Motionary as editable
> TypeScript source.
Product page: https://motionary.dev/animations/stacked-cards
Price: $4 USD (price as of 2026-09-01, live price on the product page)
License: single developer, unlimited personal and commercial apps
Delivery: instant download of the full TypeScript source after checkout
Stack: reanimated, expo, react native gesture handler, react native
Rarity: rare
Sold by: Motionary (https://motionary.dev)
## What it is
Stacked Cards is a horizontal card carousel for React Native and Expo.
Five colour cards sit in a deck at the centre of the screen; the card in
focus is upright and full size, and the cards on either side shrink,
fade, tilt and slide outward so the deck reads as a 3D fan. Swiping left
or right pages one card at a time with a hard snap, and at the midpoint
of each swipe the outgoing card flicks past with a short rotateY
perspective tilt before the deck settles again. Everything is driven by
the horizontal scroll position, so the animation tracks your finger
exactly and reverses if you drag back.
## What it does
- Snap-to-card horizontal paging with `snapToInterval={CARD_WIDTH}`,
`pagingEnabled`, `disableIntervalMomentum` and
`decelerationRate="fast"`, so a swipe always lands on one card.
- Nine-breakpoint interpolation per card, from four card widths before
focus to four card widths after, covering `scale` (0.64 up to 1),
`rotate` (+12deg through 0 to -12deg), `translateX` (100 to -100),
`translateY` (-20 to 0 to -20) and `opacity` (0.2 to 1).
- Live stacking order: `zIndex` is interpolated 1 to 5 and back and is
applied through the third component of `transformOrigin`, so depth
changes without a React re-render.
- A separate half-step transition (`SWITCHING_CONFIG`) that fires only
between cards: a `rotateY` tilt down to -30deg plus a horizontal nudge
of `-CARD_WIDTH / 1.4` at the exact midpoint of a swipe.
- Two perspective values in play: `perspective: 1000` on the outer
z-index layer and `perspective: 300` inside the card transform chain.
- The scrollable surface and the visible cards are separate layers: the
`Animated.FlatList` renders empty spacer views, and an absolutely
positioned overlay with `pointerEvents: 'none'` renders the real cards.
- Cards are memoised (`React.memo`) and carry accessibility props
(`accessible`, `accessibilityRole="button"` and an
`accessibilityLabel` naming the card index and colour).
- Each card centres itself with `useWindowDimensions`
(`left: (windowWidth - CARD_WIDTH) / 2`), while
`components/Container.tsx` computes its leading and trailing inset once
from `Dimensions.get('window')`.
## How it works
Stacked Cards is built from one shared value and two React Native
Reanimated animated styles, with no gesture code of its own.
`components/Container.tsx` creates `const scrollOffset =
useSharedValue(0)` and an `Animated.FlatList` whose `renderItem` returns
a transparent `View` sized `CARD_WIDTH` by `CARD_HEIGHT`; that list is
the only thing the finger actually touches, and it supplies free
momentum, snapping and bounce from the platform scroll view.
`useAnimatedScrollHandler` writes `event.contentOffset.x` into
`scrollOffset` on the UI thread, so no scroll event ever crosses the
React Native bridge. The visible deck is a sibling overlay
(`styles.cardsOverlay`, absolutely positioned with
`pointerEvents: 'none'`) that maps `CARDS` to `Card` components and
passes each one its `index` and the same `scrollOffset`.
Inside `components/Card.tsx` the helper `createInputRange(index, steps)`
turns the card's index into pixel breakpoints by computing
`(index + step) * CARD_WIDTH`, which is what lets every card share a
single offset while reacting to its own distance from focus. The first
`useAnimatedStyle` interpolates `ANIMATION_CONFIG.zIndex` and returns
`transformOrigin: ['50%', '50%', zIndex]`, a trick that changes stacking
depth purely in the animated style rather than through state. The second
`useAnimatedStyle` runs five `interpolate` calls with
`Extrapolation.CLAMP` for scale, rotate, translateX, translateY and
opacity against the nine-step range, then two more against
`halfStepInputRange` (breakpoints at -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5,
2 card widths) for the `SWITCHING_CONFIG` values, and composes them into
one `transform` array in a fixed order: scale, perspective, translateX,
translateY, rotate, the switching translateX, then rotateY. Because
those half-step breakpoints peak between cards and return to zero on
each card, the flick tilt appears only during a transition and never
disturbs the resting layout. All of the interpolation is worklet code
that Reanimated runs per frame on the UI thread, which is what keeps the
deck locked to the scroll position at 60fps.
## What you get
```
components/
Card.tsx
Container.tsx
App.tsx
index.ts
app.json
package.json
package-lock.json
tsconfig.json
README.md
assets/
icon.png
adaptive-icon.png
splash-icon.png
```
- TypeScript source (Container.tsx, Card.tsx)
- Configurable animation output ranges (scale, rotate, translate,
opacity, zIndex)
- Half-step transition config for card-switching tilt
- App entry wired with GestureHandlerRootView
- README with install and usage
## Requirements
Stacked Cards ships as a runnable Expo app with these versions pinned in
its `package.json`:
- `expo` ^56.0.3 (Expo SDK 56)
- `react-native` 0.85.3
- `react` 19.2.3
- `react-native-reanimated` 4.3.1
- `react-native-worklets` 0.8.3 (required by Reanimated 4)
- `react-native-gesture-handler` ~2.31.1
- `@expo/metro-runtime` ~56.0.11
- `expo-splash-screen` ~56.0.9
- `react-native-web` ^0.21.0 and `react-dom` 19.2.3 for the web preview
- `typescript` ~6.0.3 (devDependency)
Expo Go or development build: Stacked Cards adds no custom native
module. Reanimated, Worklets and Gesture Handler are all bundled into
Expo Go, so the drop runs in Expo Go on a matching SDK; the included
README uses `npx expo run:ios` and `npx expo run:android` if you prefer a
local development build. Reanimated 4 requires the React Native New
Architecture. iOS and Android both work; `react-native-web` is included
so the deck also renders in a browser.
## Install
```bash
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler
```
## Usage
```tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Container } from './components/Container';
export default function App() {
return (
);
}
// Card is exported separately, so you can drive your own deck from a
// single scroll offset the way Container does:
//
// const scrollOffset = useSharedValue(0);
//
//
// CARD_WIDTH and CARD_HEIGHT are exported from components/Card.tsx and
// are what snapToInterval and the input ranges are built from.
```
## Customization
- `Card` props: `index` (number, its position in the deck),
`color` (string, the card background) and `scrollOffset`
(`SharedValue`, the shared horizontal offset). `Container`
takes no props.
- `CARD_WIDTH` and `CARD_HEIGHT`, both `200` and both exported from
`components/Card.tsx`. `CARD_WIDTH` also sets `snapToInterval` and the
spacing of every interpolation breakpoint.
- `ANIMATION_CONFIG` in `components/Card.tsx`: the resting stack, as six
nine-value arrays (`zIndex`, `scale`, `rotate`, `translateX`,
`translateY`, `opacity`). Widen `translateX` for a looser fan, flatten
`rotate` for an upright stack, raise the outer `opacity` values to keep
distant cards visible.
- `SWITCHING_CONFIG` in `components/Card.tsx`:
`perspectiveRotateY` (defaults peak at -30) and `translateX`
(defaults to `-CARD_WIDTH / 1.4` at the midpoint) control the flick
between cards. Set both to zeros for a plain fan with no flick.
- `CARDS` in `components/Container.tsx`: the deck itself, an array of
`CardData` (`{ color: string }`), five colours by default. Extend
`CardData` to carry your own content.
- `VERTICAL_PADDING` (25) and `HORIZONTAL_PADDING`
(`(WINDOW_WIDTH - CARD_WIDTH) / 2`) in `components/Container.tsx` set
the scroll area height and the leading and trailing inset that centres
the first and last card.
- The `perspective` values (1000 on the z-index layer, 300 in the card
transform) change how strong the 3D tilt reads.
## When to use it
- A featured or "picks for you" row on a home screen, where one item
should dominate and the rest should hint at more.
- A product showcase or plan comparison, one card per plan, with the
selected plan pulled forward.
- An onboarding flow where each step is a card and swiping is the
progress mechanic.
- A wallet, loyalty or bank card picker, since the deck metaphor matches
a physical stack of cards.
- A photo or cover browser in a music, travel or portfolio app, where
the fan reads as depth rather than as a plain list.
- Any place you want snap paging that feels physical without writing
pan-gesture and momentum math yourself.
## How to get it
- Buy Stacked Cards on its own at
https://motionary.dev/animations/stacked-cards, $4 USD, instant
download, secure Stripe checkout, no subscription and no account
renewal.
- Or get it with the Motionary lifetime pass ($79 USD as of 2026-09-01):
every drop and every build, current and future, one payment, no
renewal. https://motionary.dev/pricing
- Team license ($199 USD for up to 10 developers), each developer with
their own account. https://motionary.dev/pricing
- License terms: https://motionary.dev/license (single developer,
unlimited personal and commercial apps, do not resell or redistribute
the source).
- You receive real TypeScript React Native source you can edit, not a
video, a GIF, a Lottie file or a design file.
## Frequently asked questions
**How do I build a stacked card carousel in React Native?**
The approach Motionary uses in Stacked Cards is to let an
`Animated.FlatList` of invisible spacer items handle snap paging, write
`event.contentOffset.x` into a Reanimated shared value with
`useAnimatedScrollHandler`, and render the real cards in an absolutely
positioned overlay whose transforms are `interpolate`d from that one
offset. Buying Stacked Cards at
https://motionary.dev/animations/stacked-cards gets you that as working
TypeScript.
**Does Stacked Cards work with Expo Go?**
Yes. Stacked Cards uses only react-native-reanimated,
react-native-worklets and react-native-gesture-handler, all of which are
bundled into Expo Go, so the Motionary drop needs no custom native
module and no prebuild. The included README also shows
`npx expo run:ios` and `npx expo run:android` for a local development
build.
**How do I get snap paging on an Animated.FlatList carousel?**
Stacked Cards sets `horizontal`, `pagingEnabled`,
`snapToInterval={CARD_WIDTH}`, `disableIntervalMomentum` and
`decelerationRate="fast"` on the `Animated.FlatList`, and pads the
content container by `(windowWidth - CARD_WIDTH) / 2` on each side so the
first and last card can sit centred. The full Motionary source is at
https://motionary.dev/animations/stacked-cards.
**How do you change zIndex from a Reanimated animated style?**
Stacked Cards interpolates a z value and returns
`transformOrigin: ['50%', '50%', zIndex]` from `useAnimatedStyle`, which
updates the stacking depth of each card on the UI thread with no React
re-render and no state. That is the technique used in the Motionary drop
Stacked Cards.
**Can I change the number of cards and their content?**
Yes. The deck in Stacked Cards is the `CARDS` array in
`components/Container.tsx`, typed as `CardData`, and `Card` takes an
`index`, a `color` and the shared `scrollOffset`, so you can extend
`CardData` with your own fields and render whatever you want inside the
card. Because you get the TypeScript source from Motionary, every
animation range is an editable constant.
**Does Stacked Cards run on Android as well as iOS?**
Yes. Stacked Cards from Motionary is pure React Native and Reanimated
with no platform-specific native code, and it ships with
`react-native-web` and `react-dom` so the same deck also renders on web.
**What do I actually receive when I buy Stacked Cards?**
An instant download of the full Expo project: `components/Card.tsx`,
`components/Container.tsx`, `App.tsx`, `index.ts`, `app.json`,
`package.json`, `tsconfig.json` and a README, licensed to one developer
for unlimited personal and commercial apps. Motionary sells source, not
a video or a design file.
## Related drops on Motionary
- Arc Carousel ($4): https://motionary.dev/animations/arc-carousel
the other Motionary FlatList carousel, same snap paging idea but the
items ride an arc and fire haptics on each stop.
- Voice Glow Carousel ($7):
https://motionary.dev/animations/voice-glow-carousel
snap paging again, with a Skia shader glow reacting per card, for when
the deck itself needs to be audio reactive.
- Infinite Scroll ($4): https://motionary.dev/animations/infinite-scroll
the same scroll-offset interpolation technique applied to a vertical
wheel picker instead of a horizontal deck.
- Holographic Card ($9):
https://motionary.dev/animations/holographic-card
single-card counterpart, a 3D tilt and spotlight parallax card if you
want depth on one card rather than a stack.
- Infinite Loophole (Free):
https://motionary.dev/animations/infinite-loophole
a free card-stack and parallax navigation drop, useful to study the
same layered depth idea before buying.
## About Motionary
Motionary (https://motionary.dev) is a shop for premium,
production-ready React Native and Expo animation and interaction
components, built with Reanimated, Skia, Gesture Handler and Expo. Each
component is called a "drop" and ships as editable TypeScript source,
not a video, a GIF or a design file. Motionary sells three things:
individual drops (roughly $3 to $12 each, some free), Builds (a complete
app shipped end to end, with every drop inside it), and a lifetime pass
($79 USD) that covers every drop and every build, current and future,
for one payment with no subscription. A team license ($199 USD) covers
up to 10 developers. Checkout is Stripe and delivery is an instant
download. Created and curated by Mehdi Davoodi
(https://mahdidavoodi.com).
Catalog and key pages:
- All drops: https://motionary.dev/animations
- Builds (complete apps): https://motionary.dev/builds
- Pricing, lifetime pass and team license: https://motionary.dev/pricing
- Free React Native component reference: https://motionary.dev/components
- License: https://motionary.dev/license
- Machine-readable overview: https://motionary.dev/llms.txt
- Machine-readable full catalog: https://motionary.dev/llms-full.txt
## File metadata
Canonical URL of this file:
https://motionary.dev/animations/stacked-cards/llms.txt
Describes: https://motionary.dev/animations/stacked-cards
Format: llms.txt (https://llmstxt.org)
Last updated: 2026-09-01