@kaktos/flipbook-react
v1.0.5
Published
3D page flip effect for React
Maintainers
Readme
@kaktos/flipbook-react
React component for 3D page flip effect. Supports touch/mouse/pointer gestures, multi-level zoom, keyboard navigation, and hi-res image switching.
Install
npm i @kaktos/flipbook-reactQuick Start
import '@kaktos/flipbook-react/style.css'
import { Flipbook } from '@kaktos/flipbook-react'
function App() {
return (
<div style={{ width: 800, height: 600 }}>
<Flipbook
pages={[
'/images/1.jpg',
'/images/2.jpg',
'/images/3.jpg',
'/images/4.jpg',
]}
/>
</div>
)
}
style.cssis required. The component fills its parent container's width and height, so the parent must have explicit dimensions.
Cover Page
Pass null as the first element in pages to use a single-page cover:
<Flipbook
pages={[
null, // cover placeholder (renders as single page)
'/images/1.jpg',
'/images/2.jpg',
'/images/3.jpg',
]}
/>Render Props (Custom Controls)
Use the children render prop to build custom UI controls. The function receives the current state and action methods:
<Flipbook pages={pages}>
{({ page, numPages, canFlipLeft, canFlipRight, flipLeft, flipRight }) => (
<div className="toolbar">
<button onClick={flipLeft} disabled={!canFlipLeft}>Prev</button>
<span>{page} / {numPages}</span>
<button onClick={flipRight} disabled={!canFlipRight}>Next</button>
</div>
)}
</Flipbook>The render prop provides:
| Property | Type | Description |
|---|---|---|
| page | number | Current page number (1-based) |
| numPages | number | Total number of pages |
| canFlipLeft | boolean | Whether left flip is possible |
| canFlipRight | boolean | Whether right flip is possible |
| canZoomIn | boolean | Whether zoom in is possible |
| canZoomOut | boolean | Whether zoom out is possible |
| flipLeft() | function | Flip to the left |
| flipRight() | function | Flip to the right |
| zoomIn() | function | Zoom in one level |
| zoomOut() | function | Zoom out one level |
| goToPage(n) | function | Jump to page n |
Imperative Ref
For programmatic control outside of render props, use a ref:
import { useRef } from 'react'
import type { FlipbookRef } from '@kaktos/flipbook-react'
function App() {
const ref = useRef<FlipbookRef>(null)
return (
<>
<button onClick={() => ref.current?.goToPage(5)}>Go to page 5</button>
<Flipbook ref={ref} pages={pages} />
</>
)
}The ref exposes the same properties and methods as the render prop.
Hi-Res Images
Provide pagesHiRes for high-resolution images that load when the user zooms in:
<Flipbook
pages={['/images/1.jpg', '/images/2.jpg']}
pagesHiRes={['/images-large/1.jpg', '/images-large/2.jpg']}
zooms={[1, 2, 4]}
/>Standard images display at zoom === 1. Hi-res images swap in automatically when zoomed.
Controlled Page
By default the component manages page state internally. To control it from outside, pass page and onPageChange:
const [page, setPage] = useState(0)
<Flipbook
pages={pages}
page={page}
onPageChange={setPage}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| pages | (string \| null)[] | required | Array of page image URLs. null for cover placeholder. |
| pagesHiRes | (string \| null)[] | [] | Hi-res image URLs, swapped in when zoomed. |
| pageLabels | string[] | — | Alt text for each page image. Defaults to "Page N". |
| flipDuration | number | 1000 | Flip animation duration in ms. |
| zoomDuration | number | 500 | Zoom animation duration in ms. |
| zooms | number[] \| null | [1, 2, 4] | Available zoom levels. null disables zoom. |
| perspective | number | 2400 | CSS perspective value for 3D effect. |
| nPolygons | number | 10 | Number of polygons per page for the flip curve. |
| ambient | number | 0.4 | Ambient lighting intensity (0–1). |
| gloss | number | 0.6 | Specular gloss intensity (0–1). |
| swipeMin | number | 3 | Minimum swipe distance (px) to trigger a flip. |
| singlePage | boolean | false | Force single-page mode even in landscape. |
| forwardDirection | 'left' \| 'right' | 'right' | Reading direction. |
| centering | boolean | true | Auto-center the book in the viewport. |
| startPage | number \| null | null | Initial page number (1-based). |
| loadingImage | string | Built-in spinner | Image shown while pages are loading. |
| clickToZoom | boolean | true | Enable click/tap to zoom. |
| dragToFlip | boolean | true | Enable drag/swipe to flip. |
| wheel | 'scroll' \| 'zoom' | 'scroll' | Mouse wheel behavior when zoomed. |
| bookClassName | string | — | Class applied to the book frame around the pages. |
| bookStyle | React.CSSProperties | — | Inline styles applied to the book frame around the pages. |
| viewportClassName | string | — | Class applied to the viewport element. |
| viewportStyle | React.CSSProperties | — | Inline styles applied to the viewport element. |
| page | number | — | Controlled page state (internal index). |
| onPageChange | (page: number) => void | — | Called when page changes in controlled mode. |
| onFlipLeftStart | (page: number) => void | — | Fired when left flip begins. |
| onFlipLeftEnd | (page: number) => void | — | Fired when left flip completes. |
| onFlipRightStart | (page: number) => void | — | Fired when right flip begins. |
| onFlipRightEnd | (page: number) => void | — | Fired when right flip completes. |
| onZoomStart | (zoom: number) => void | — | Fired when zoom animation begins. |
| onZoomEnd | (zoom: number) => void | — | Fired when zoom animation completes. |
The component also accepts all standard <div> HTML attributes (className, style, id, data-*, aria-*, etc.) which are spread onto the root element.
Keyboard Navigation
When the viewport is focused:
| Key | Action |
|---|---|
| ArrowLeft | Flip left |
| ArrowRight | Flip right |
| + / = | Zoom in |
| - | Zoom out |
The left/right click areas are also keyboard-accessible with Enter or Space.
CSS Customization
The component ships with structural CSS that is required for 3D transforms and layout. Visual defaults can be overridden with CSS variables:
:root {
--flipbook-blank-page-bg: #f5f5f5; /* blank page background, default: #ddd */
}You can also pass className and style to the root element:
<Flipbook className="my-book" style={{ background: '#1a1a1a' }} pages={pages} />Book Shadow / Inner Styling
If you want to style the book frame around the pages (for shadows, borders, etc.), use
bookClassName / bookStyle:
<Flipbook
pages={pages}
bookStyle={{ boxShadow: '0 24px 60px rgba(0,0,0,0.35)', borderRadius: 8 }}
/>Or with a class:
<Flipbook pages={pages} bookClassName="bookShadow" />.bookShadow {
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
border-radius: 8px;
}Viewport Styling
If you want to control the viewport height/overflow (for example to avoid full-height),
use viewportClassName / viewportStyle:
<Flipbook
pages={pages}
viewportStyle={{ height: 500 }}
/>Styling Layers (How to Choose)
The component has three styling layers. Pick the one that matches what you want to change:
- Root (
className/style)
Outer wrapper. Use for overall layout (centering, margins, container sizing). - Viewport (
viewportClassName/viewportStyle)
The interactive area that handles gestures/zoom. Use to control height, overflow, or alignment within the root. - Book Frame (
bookClassName/bookStyle)
The book’s visual frame around pages. Use for shadows, borders, and background decoration.
Recommended usage order:
- Prefer sizing the parent container first (simple and predictable).
- If you need internal sizing, use viewport props.
- For visual effects, use book props.
License
MIT
