ui-059

PDF Flipbook

£5

A PDF flipbook: point it at a PDF and read it in two-page spreads, dragging each page over with a real paper curl, momentum and selectable text.

DraggableInertiaPlugin advanced
7 more details
page-turn3d-transformsdraggablemomentumcrossfadekeyboard-navigationfocus-management
PDF Flipbook - UI component preview

This element reads better at your own screen size than in the frame below:

About this component

Turn any PDF into a book people want to page through. Give the component a PDF and it lays it out as spreads, the cover alone on the right, then facing pages, with the back cover alone at the end. Drag a page by its edge and it bends around a moving fold, with lighting down the curl and a shadow crossing the page beneath. Let go and it throws over with momentum; flick hard and the pages riffle.

Read the full effect overview

Once a spread settles, PDF.js's text layer sits on top, so text can be selected, links work and screen readers read the page. A compact bar holds page buttons, a page field, a thumbnail strip, download and full screen. On phones it becomes one page at a time; with reduced motion, spreads crossfade. The demo reads an original sixteen-page seasonal garden planner.

What's included

11 items
  • Two-page spreads from any PDF: cover alone on the right, facing pages, an even last page alone on the left
  • Page curl built from vertical strips along a fold, with the real next page on the back of each turning leaf
  • Drag a page edge to turn it; InertiaPlugin throws it with page snapping, and a hard flick riffles on
  • Settled spreads carry the PDF.js text and link layers: selectable text, working links, readable by screen readers
  • Click the outer half of a page, arrow keys, Page Up/Down, Home and End, all queued so fast input is never dropped
  • Page field and a keyboard-operable thumbnail strip; short jumps riffle, long jumps crossfade
  • One page at a time on narrow widths, crossfades under reduced motion, and a polite live region for every turn
  • Bounded rendering: each page drawn once at device resolution, only nearby pages kept, far bitmaps released
  • Download, full screen and an optional scrolling-document link
  • Independent instances with goTo, next, prev and destroy, plus root-scoped ready and turn events
  • Light and dark themes; pages keep the PDF's own colours

Perfect for

4 use cases
  • Catalogues, lookbooks and brochures that were designed as spreads
  • Magazines, zines and annual reports published as PDF
  • Menus, programmes and event guides
  • Portfolios and case-study decks presented as a book

How it works

1 section

PDF.js renders each page once to a bitmap at device resolution. Every leaf is a stack of vertical strips, each showing its slice of that bitmap, laid along a fold polyline so the paper bends; per-strip lighting and a cast shadow do the rest. Draggable drives the turn from an off-screen proxy and InertiaPlugin snaps it to a page. When the spread settles, the flat pages return with PDF.js text and link layers on top. A small queue renders only the pages a turn from here can show and releases the rest.

Plugins Draggable, InertiaPlugin
Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Paid UI element

Purchase to unlock the code.

Buying PDF Flipbook opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse UI elements

Documentation

What's Included

  • index.html: the demo page and the markup to copy
  • assets/script.js: readable, commented source with an onReady guard
  • assets/style.css: component styles, theme tokens and the PDF.js text/link layer rules
  • assets/vendor/pdfjs/: PDF.js 5.4.624 (Apache-2.0), its worker and resource folders, loaded on demand
  • assets/small-garden-year.pdf: the original demo document, and source/generate-book.py, the ReportLab script that draws it

What it does:

  • Spreads from any PDF. Page 1 is the cover, alone on the right; then 2|3, 4|5 and so on; an even final page sits alone on the left as the back cover. The spread slides half a page so a lone cover is centred.
  • A real page curl. Each turning leaf is a row of vertical strips laid along a fold polyline, lit per strip, with a shadow crossing the page beneath. The back of the turning leaf is the next PDF page, not a mirror of the front.
  • Drag, throw, riffle. Pick a page up by its outer edge and drag it over. InertiaPlugin throws it and snaps to a page, a lazy nudge falls back, and a hard flick carries one or two more pages with it.
  • Text you can use. When a spread settles, PDF.js's text and link layers sit over the flat pages: text is selectable, internal links turn to their page, external links open, screen readers read it.
  • Bounded rendering. Each page is drawn once at device resolution into a bitmap that the strips slice. Only the pages a turn from here can show are rendered, one at a time, nearest first; far bitmaps are released.
  • Controls. Previous and next, a page field ("Page 4 of 16"), a thumbnail strip, download and full screen, plus an optional link to a scrolling version.
  • One page on narrow widths. Below 620px of component width the book shows one page, hinged on its left edge, with fewer strips.

Quick Start

Serve the folder over HTTP or HTTPS. PDF.js loads as an ES module with a worker, which browsers refuse from a file:// URL.

1. Add to your <head>:

Code snippet omitted: it ships with the download.

2. Add the flipbook where it should appear. Give it a height (the book scales to fit both the width and the height it is given). The link inside is what a visitor without JavaScript gets; the script replaces it and puts it back on destroy().

Code snippet omitted: it ships with the download.

3. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

Every [data-pdf-flipbook] element mounts itself on load. Keep assets/vendor/pdfjs/ beside assets/script.js: the script finds PDF.js relative to its own URL, so the pair works from any folder.

The PDF must be same-origin or served with CORS headers (Access-Control-Allow-Origin covering your site). PDF.js fetches the file itself; a cross-origin PDF without CORS fails to open, and the flipbook shows a message with a direct link instead.

The toolbar strip at the top of index.html (the prompt and the Light/Dark switch) and the .stage-ground wrapper are demo furniture, not part of the component; leave them behind.

Keyboard & Accessibility

Key What it does
ArrowRight / PageDown Next spread (queued, so repeated presses are never dropped)
ArrowLeft / PageUp Previous spread
Home / End First page / last page
Tab Moves from the pages to the page links, then through the controls
Enter in the page field Jumps to that page
Escape in the page field Restores the current page number
In the thumbnail strip: ArrowLeft / ArrowRight, Home / End Move between spreads
In the thumbnail strip: Enter / Space Jump there; the strip closes and focus returns to its button
In the thumbnail strip: Escape Close; focus returns to the button
  • The root is role="region" with aria-roledescription="flipbook" and the PDF's title as its label.
  • The pages are a focusable role="group" labelled with what is showing ("Pages 4–5"); each page inside it is a group labelled "Page 4" holding the PDF.js text layer and link layer. The 3D leaves and strips are pictures and are aria-hidden.
  • A polite role="status" live region announces every settled turn: "Pages 4–5 of 16".
  • Every control is a real <button> or link with an accessible name. The thumbnail button has aria-expanded and aria-controls; the current spread in the strip has aria-current="true"; full screen is a toggle with aria-pressed.
  • Focus rings are 2px in the accent on every control and around the pages. If focus is on a link inside a page when a turn starts, it moves to the pages group so the keyboard never loses its place.
  • prefers-reduced-motion: no curl and no drag physics. Every turn, click and key becomes a short crossfade between spreads, and the controls work exactly the same.
  • Click (or tap) the outer half of a page to turn it. Clicks on text are left alone on desktop so text can be selected; on touch the whole outer half turns.

Options

Set as data- attributes on the root, or as options to PdfFlipbook.mount(root, options).

Attribute Option Default What it does
data-src src The PDF to open (required). Same-origin or CORS-enabled.
data-title title the PDF's own title Accessible name of the region
data-linear-href linearHref none Adds a link to the bar that opens in a new tab, so the reader keeps their place in the book; point it at the PDF itself, an HTML edition or a scrolling PDF viewer
data-linear-label linearLabel Open PDF Visible text of that link; change it when the link is not the PDF (for example "Read online")
data-start-page startPage 1 Page to open on
data-single-below singleBelow 620 Component width (px) below which one page shows at a time
data-max-spread maxSpread 1040 Largest spread width (px)
data-strips strips 16 Strips per leaf; smoother curl, more DOM (9 at most in single-page layout)
data-curl curl 48 Degrees the free edge leads the spine mid-turn: 0 is a flat card, 50 is magazine stock
data-riffle="false" riffle: false on Stop a hard flick carrying extra pages
data-invite="false" invite: false on Skip the single peel on load that shows the page can be picked up
data-pdf-flipbook="manual" Skip the automatic mount; call PdfFlipbook.mount() yourself

API and Events

Code snippet omitted: it ships with the download.

Events are dispatched on the root element:

Code snippet omitted: it ships with the download.

Instances are independent: several flipbooks on one page each keep their own state, queue and cleanup.

Themes

Two themes, light and dark, on the same design. Set the attribute on <body> or on the flipbook root:

Code snippet omitted: it ships with the download.

With no attribute anywhere, the light tokens apply. The demo's switcher and its ?variant=dark URL parameter only set that attribute.

The tokens, set in both theme blocks at the top of assets/style.css: --ground, --ground-2, --raised, --hover, --ink, --ink-2, --ink-3, --line, --line-strong, --accent, --shadow, --pfb-page-shadow (the book on the ground), --pfb-gutter (strength of the spine shadow), --pfb-selection (text selection on the pages), --font, --radius, --radius-sm. To match your brand, re-value them; nothing in the script reads a theme name.

The accent has one job: focus rings, the current thumbnail and the pressed state of a control. It is #ff4d00 on dark and #e64500 on light, one shade deeper so it clears 3:1 against the light ground. Page paper is never themed: pages keep the PDF's own colours.

How It Works

The curl. Each leaf's strips sit along a fold polyline: every strip starts where the previous one ended, the free edge leads and the spine follows (curl degrees apart at mid-turn), which is what makes it read as paper rather than a rotating card. Each strip side is two background layers, a lighting gradient for its angle over its slice of the page bitmap, so a highlight travels down the fold and the turned-away side darkens. Strip DOM is only built the first time a leaf moves.

Rendering. PDF.js renders a page once, at device pixels (capped), and the bitmap becomes an object URL that the flat face and every strip share. The queue draws one page at a time in order of how soon a turn could show it, and releases bitmaps outside the current spread plus or minus two leaves. A resize stretches the old bitmaps until sharp ones land.

The turn. One number drives it: Draggable tracks an element that never enters the page, negative turns the next leaf and positive pulls the previous one back. snap lands on a page, so InertiaPlugin's projected throw decides whether a release commits. A scripted turn (a key, a click, goTo) tweens the same number, and extra requests queue instead of being dropped.

Settling. When a turn commits, the strips are put away, the flat faces return and the reading layer is rebuilt for the new spread from PDF.js's TextLayer and AnnotationLayer. That layer is flat and never 3D-transformed, which is why selection, links and screen readers behave as they do on any page.

Scope. This is a book, not a document viewer: there is no search, zoom or reading mode, and no local file input. For those, see the PDF Document Viewer.

Customisation

  • Change the book's size by sizing the root; data-max-spread caps it on wide screens.
  • Paper character is data-curl; smoothness against DOM cost is data-strips.
  • The spine shadow is --pfb-gutter; the book's drop shadow is --pfb-page-shadow.
  • Turn speed and easing are the animateTurn tween in assets/script.js; the crossfade length is in crossfade.
  • The bar is plain markup built by createFlipbook; remove a control by deleting its three lines there.

Requirements

  • GSAP 3.12+ with Draggable and InertiaPlugin
  • PDF.js is bundled in assets/vendor/pdfjs/; keep its licence files when redistributing
  • Served over HTTP(S); the PDF same-origin or CORS-enabled
  • No build step, no framework

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.