A natural-feeling, paper-like page-turn animation for React Native. It combines continuous page geometry, gesture-aware motion, settling and reversion, and Skia rendering instead of playing a canned transition. Image galleries, rasterized PDF pages, and document readers can share the same animation path.
Real integration footage from the Persimmon reader on iPad.
Alpha: the package boundary, clean-consumer install, and complete example are validated on the compatibility matrix below. Expect small API changes before 1.0.
- The page bends as a continuous rolled strip rather than a flat card or a corner-only warp.
- Drag distance, direction, velocity, and release intent feed the same motion model used by automatic turns.
- A page can commit or settle back naturally instead of snapping at one fixed threshold.
- Perspective, front/back textures, paper color, and dynamic shadows preserve the visual cues of a turning sheet.
naturalis the baseline preset; paper weight, curl relaxation, velocity, timing, and commit behavior remain tunable when a product needs a different material feel.
“Natural” is the design goal, not a claim of laboratory-grade paper simulation.
Install the renderer and its native peer dependencies:
pnpm add @chihumyum/react-native-natural-page-turn
npx expo install @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-workletsWrap the application root with GestureHandlerRootView. Expo projects include
the Worklets transform; React Native Community CLI projects must add
react-native-worklets/plugin last in babel.config.js. Rebuild the native app
after installing the peer dependencies.
PageTurnPager loads adjacent pages, caches them, commits page indices, and
supports both direct gestures and imperative playback:
import {
PageTurnPager,
imagePageSource,
type PageTurnPagerHandle,
} from "@chihumyum/react-native-natural-page-turn";
import { useImage } from "@shopify/react-native-skia";
import { useMemo, useRef } from "react";
const first = useImage(require("./assets/first.png"));
const second = useImage(require("./assets/second.png"));
const pager = useRef<PageTurnPagerHandle>(null);
const source = useMemo(
() => (first && second ? imagePageSource([first, second]) : undefined),
[first, second],
);
if (!source) return null;
<PageTurnPager
ref={pager}
source={source}
style={{ flex: 1 }}
preset="natural"
onPageChange={(pageIndex) => console.log(pageIndex)}
/>;
pager.current?.next();
pager.current?.previous();Use width and height for a fixed canvas, or give the pager a measurable
style. gestureDirection selects which direction a drag claims; the imperative
handle can play either direction.
Use lazyImagePageSource() when pages are decoded on demand:
const source = lazyImagePageSource({
pageCount: document.pageCount,
async loadPage(index, size, signal) {
return renderDocumentPageToSkImage(document, index, size, signal);
},
releasePage(_index, image) {
image.dispose();
},
});The library deliberately does not parse PDF or EPUB. Rasterize a requested page
to SkImage in the adapter and keep parsing, pagination, and network work out
of the gesture hot path. See content adapters.
- Package root:
PageTurnPager, low-levelPageTurn, image page sources, presets, and stable public types. @chihumyum/react-native-natural-page-turn/advanced: mesh, shaders, native-frame helpers, tuning ranges, and renderer internals for custom integrations.@chihumyum/react-native-natural-page-turn/core: platform-neutral solver, gesture state, timing, reversion, and worklet implementation.
Most applications should install only the renderer package. Its dependency on
@chihumyum/page-turn-core is resolved automatically.
Start with natural, light, heavy, or snappy, then override only the
values the product needs:
<PageTurnPager
preset="heavy"
tuning={{ gestureCommitThreshold: 0.82 }}
source={source}
/>See the tuning guide before exposing raw values in a product UI.
The initial validated matrix is intentionally narrow:
| Dependency | Validated version |
|---|---|
| React Native | 0.86.2 |
| React | 19.2.3 |
| React Native Skia | 2.6.2 |
| Reanimated | 4.5.1 |
| Worklets | 0.10.3 |
| Gesture Handler | 2.32.0 |
The package uses only public APIs from these projects. Wider peer ranges are accepted but should be treated as unverified until added to the consumer test matrix.
Run the image-based Expo example:
pnpm install
pnpm --filter @page-turn-example/basic startThe repository also includes a self-contained
PDF and EPUB document demo. Its bundled fixtures work
out of the box, and its adapters show the complete path from a document page to
SkImage and then to PageTurnPager:
pnpm install
pnpm --filter @page-turn-example/documents prebuild
pnpm --filter @page-turn-example/documents ios
# or
pnpm --filter @page-turn-example/documents androidBecause the PDF adapter contains native PDFKit/PdfRenderer code, use a local
development build rather than Expo Go. To test your own files, run seed:pdf or
seed:epub as described in the
document demo README.
Run all source, build-output, archive, and clean-consumer checks:
pnpm checkThe pack check builds both packages, confirms workspace dependencies were converted to registry versions, installs the tarballs into a clean temporary consumer, and type-checks the root and advanced entry points.
For setup failures, see troubleshooting.
MIT
