React PDF viewer with page-curl animation, full UI chrome, and SSR-safe hooks.
Quick Start ·
Features ·
Docs ·
publi (paid) ·
License
A modern React PDF viewer that ships ready: built-in toolbar, page curl animation, thumbnails, fullscreen, print, download. Every interaction is keyboard-accessible with WAI-ARIA, themed light/dark, and SSR-safe on Next.js / Remix / Gatsby. Drop <Flipbook url="..." /> into any React 18+ app — no plugin composition, no toolbar wiring, no pdf.js example surgery.
Built for product documentation portals, magazine + brochure CMSes, e-book apps, archival viewers, and embed-in-iframe widgets. TypeScript-first, MIT licensed.
Install the package and its peer dependencies:
npm install @flipbookjs/react-viewer@1.0.0 pdfjs-dist react react-domRender the viewer:
import { Flipbook } from '@flipbookjs/react-viewer';
import '@flipbookjs/react-viewer/styles.css';
export default function Reader() {
return (
<Flipbook
url="/document.pdf"
documentName="My Document"
enablePageCurl
/>
);
}Toolbar, page navigation, zoom, fullscreen, print, download, thumbnails, and the page curl animation all render by default.
For Next.js, Remix, Gatsby, and other SSR frameworks, see SSR integration in MIGRATION.md §13.
<Flipbook url="..." />drops in a complete viewer — toolbar, navigation, zoom, fullscreen, print, download, thumbnails, theme. No setup, no plugins, no glue.- Page-curl animation — opt in with
enablePageCurl; tactile dual-page interaction in dual-cover mode. - Built-in toolbar — every action accessible via keyboard with WAI-ARIA roving-tabindex;
aria-pressedon toggles; reduced-motion respected on animated parts. - Thumbnail panel — virtualized horizontal scroll, click-to-navigate, slide animation that defers to
prefers-reduced-motion. - Streaming print — per-page render keeps memory bounded; cancellable mid-flight; typed errors (
PrintLimitExceededError); analytics callbacks (onPrintStart,onPrintComplete,onPrintError,onPrintAbort). - Smart download — derives filename from
documentNameor URL basename; sanitizes OS-illegal characters; URL-decoded to strip%20-style noise. - Theme runtime — light/dark via
initialTheme+onThemeChange; restyle with--fbjs-*CSS variables, no class overrides needed.
- Toolbar parts — 12 button components +
<ToolbarShell>exported from@flipbookjs/react-viewer/toolbar-parts. Build your own bar; keep the accessibility. - Public hooks —
useFlipbook()for full state,useFlipbookSelector()for narrow subscriptions,useFlipbookActions()for stable-identity dispatch,shallowEqualhelper. - Custom PDF sources — implement the
PageSourceinterface forUint8Arraydocuments, custom HTTP, or pre-rendered image tiles via publi.
- TypeScript-first — every prop, hook, action, and type fully declared. No
any, no implicit erasure. - SSR-safe — frozen sentinels for server renders; concrete patterns for Next.js App Router / Pages Router / Remix / Gatsby in MIGRATION.md §13.
- React 18+ — uses
useSyncExternalStorefor the snapshot store; safe under concurrent rendering and Strict Mode.
- MIGRATION.md — full integration guide. Every prop with type/default/semantic, default-flip notes, iframe embedding requirements (sandbox tokens + Permissions Policy +
allowfullscreen), toolbar-parts composition examples, theme/print/download integration patterns, API stability contract, and SSR / Next.js / Remix integration with concrete wrapper patterns. dist/index.d.ts— full TypeScript declarations; available after install atnode_modules/@flipbookjs/react-viewer/dist/index.d.ts.
- React 18+ —
useSyncExternalStoresnapshot store for SSR-safe state. - TypeScript — strict-mode typed throughout.
- pdf.js — Mozilla's PDF renderer (peer dependency,
^5.6.0). - Vite — bundler + dev server for the package and the demo app.
- vitest — test runner.
publi is a (paid) hosted rendering service working perfectly with this viewer.
What you get:
- Sub-second first paint on every device — pre-rendered load from CDN, no pdf.js boot.
- Skip the pdf.js bundle — pre-rendered sources don't load it.
- Search-ready out of the box
- Constant client memory — lazy fetch per page, even for 1,000-page documents.
- Render once, serve forever — every viewer mount hits the same warm CDN cache, regardless of user device or location.
- Re-render on source change — webhook your storage; publi re-rasterizes and pushes new artifacts to CDN.
Drop in our client adapter and swap two lines:
import { Flipbook } from '@flipbookjs/react-viewer';
import { PreRenderedPageSource } from '@flipbookjs/api-adapter';
import '@flipbookjs/react-viewer/styles.css';
<Flipbook source={new PreRenderedPageSource({ documentId: 'doc_abc123' })} />| Default (pdf.js) | publi pre-rendered | |
|---|---|---|
| Time to first page | Boot pdf.js + download PDF + render | Single CDN fetch of a static page image |
| Bundle weight | ~2 MB pdf.js worker per load | Just the viewer + image bytes per page |
| Low-end mobile | CPU-bound rasterization, can stall | Plain images — runs everywhere |
| Text search | Per-page extraction in the browser | Pre-indexed server-side, returned with pages |
| Caching | Per-client browser cache | Shared CDN cache across all viewers + devices |
| Long documents | Render-on-scroll, memory grows | Lazy fetch per page, constant memory |
The viewer talks to any rendering source via its PageSource interface. See PdfjsSource for the reference implementation and dist/index.d.ts for the interface contract.
PRs welcome. The repo layout:
packages/viewer/ React viewer component (published to npm)
demo/ Vite-powered demo app — `npm run dev` for local development
To run the demo locally:
git clone https://github.com/flipbookjs/react-flipbook.git
cd react-flipbook
npm install
cd demo && npm run devThen open http://localhost:5173. The demo exercises every consumer-facing prop as a smoke check.
Before opening a PR, please run from packages/viewer/:
npm test -- --run # vitest suite
npx tsc --noEmit # type check
npx eslint src/ # lint
npm run build # build the viewerOpen an issue for bugs or feature discussion.
MIT. © 2026 flipbookjs.