npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kaktos/flipbook-react

v1.0.5

Published

3D page flip effect for React

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-react

Quick 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.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:

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

  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