npm package: @kaktos/flipbook-react
Description: 3D page flip effect for React
Latest version: 1.0.5

# @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

```bash
npm i @kaktos/flipbook-react
```

## Quick Start

```tsx
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.css` is 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:

```tsx
<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:

```tsx
<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:

```tsx
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:

```tsx
<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`:

```tsx
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:

```css
:root {
  --flipbook-blank-page-bg: #f5f5f5; /* blank page background, default: #ddd */
}
```

You can also pass `className` and `style` to the root element:

```tsx
<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`:

```tsx
<Flipbook
  pages={pages}
  bookStyle={{ boxShadow: '0 24px 60px rgba(0,0,0,0.35)', borderRadius: 8 }}
/>
```

Or with a class:

```tsx
<Flipbook pages={pages} bookClassName="bookShadow" />
```

```css
.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`:

```tsx
<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:

1. Prefer sizing the **parent container** first (simple and predictable).
2. If you need internal sizing, use **viewport** props.
3. For visual effects, use **book** props.

## License

MIT