Options

Constructor options for new Zine(container, options). Only source is required; the rest have sensible defaults.

import { Zine, ImageSource } from '@zinejs/core'

new Zine(document.getElementById('book'), {
  source: new ImageSource([...urls]),
  // only source is required; everything below is optional
})

Core

sourcerequired · Source
Content that resolves page indices to rasters. Use ImageSource or PdfSource (from @zinejs/pdf).
rendererdefault 'auto'
'auto' prefers WebGL2 and falls back to CSS. Force 'webgl2' or 'css', pass an ordered list, or a custom Renderer.
curldefault 'cone' · WebGL2 only
Page-curl model. Bundled names: 'cone' (default) and 'simple'. Optional models ( roll, leaf, flick, silk) are imported from @zinejs/core/curls and passed as objects so unused curls stay out of the bundle. CSS ignores this.
spreadModedefault 'cover'
double, single, cover (lone first), book (lone first and last).
directiondefault 'ltr'
'ltr' or 'rtl'.
startPagedefault 0
Zero-based page to open on. A deep-link hash takes precedence when deepLink is on.
flipDurationdefault 800 ms
Flip animation length for a two-page spread. Lone pages stretch this and ease out harder.
width / heightoptional · px
Fixed container size. Omit to size from CSS.

Interaction & layout

clickToFlipdefault 'edge'
'edge', 'half', or 'off'.
clickZoneSizedefault 64 px
Edge zone width when clickToFlip is 'edge'.
clickFlipDelaydefault auto
Delay before a click flips so double-click zoom can win. Auto is 0 or 250 when double-click zoom is active in the flip zone.
cursorHintsdefault true · mouse only
Change the cursor over the book to hint what a press does: pointer where a click turns, zoom-in where double-click zooms, grab / grabbing when zoomed or peeling. Pass false to keep the default cursor. No effect on touch.
hintsdefault true
One-shot cues on the book itself (corner peek, idle nudge, zoom/pan captions), including for touch. Each hint stops once the reader performs the gesture. Motion cues honour prefers-reduced-motion. false turns them off. { persist: true } remembers learned gestures in localStorage across visits.
singlePageThresholddefault 640 px
Below this container width the book shows one page per spread (when responsiveSpread is true).
responsiveSpreaddefault true
Whether a narrow container may override spreadMode. Set false to keep two-page spreads even on small screens. Changeable later with setResponsiveSpread().
deepLinkdefault true
Keep the current page in the URL hash ( #page=12) for shareable links and back/forward. Only the page key is touched. Pass false if your app owns the hash. Only one book per page claims the hash.
disableContextMenudefault false
Suppress the browser right-click menu over the book with nothing in its place. A mild deterrent, not protection. Mutually exclusive with contextMenu (enabling both throws).
contextMenudefault true
Right-click menu of reading controls at the cursor (zoom, page turns, fullscreen, print, download, share). Works with controls: false too. Mouse only. false keeps the browser menu. Or pass { items, colorScheme } ( colorScheme follows the toolbar when both are mounted).
hideControls
Control ids to remove from the toolbar and the context menu at once, wherever they sit (bar, overflow, or right-click). Example: ['print', 'download']. Only removes; rearrange with each surface's items.
loadingdefault true
Overlay while a URL PDF downloads and the first spread prepares. false hides it. Drive your own from the progress event and ready (see API). Retheme with --zine-loading-bg, --zine-loading-fg, --zine-loading-accent, and --zine-loading-track.

Controls

Built-in toolbar (default on), docked below the book so it never covers a page. The toolbar chunk is lazy: a book with controls: false never downloads it.

controlsdefault true
false hides the toolbar. Or pass an object:
controls.positiondefault 'bottom'
'top' | 'bottom' | 'left' | 'right'.
controls.dockeddefault true
Sit outside the book (sibling wrapper, container size unchanged). false floats the toolbar over the book.
controls.items
Replaces the default layout. Built-ins: prev, next, first, last, pageInput, zoomIn, zoomOut, search, thumbnails, outline, spread, print, download, share, fullscreen, menu. Use '|' for separators. Register custom ones with defineControl.
controls.arrowsdefault true
Large page-turn arrows beside the book (not over it). On viewports under 640px they overlay the book edges instead of hiding. false turns them off. 'desktop' shows them only on wide screens; 'mobile' only on narrow ones. The split follows the same 640px breakpoint as the layout, live on resize.
controls.colorSchemedefault 'auto'
'auto' follows the page color-scheme. 'light' or 'dark' pins the toolbar, arrows, panels, and share dialog regardless of the page.
controls.className
Extra class on the toolbar root, for your own CSS.

Default items:

['prev', 'pageInput', 'next', '|', 'zoomOut', 'zoomIn', 'search', 'share', 'menu', 'fullscreen']

menu is the overflow, holding first, last, spread, thumbnails, outline, print, and download. Controls hide when they cannot work (search without text, download/print without a file, fullscreen without the API).

new Zine(el, { source, controls: false })
new Zine(el, { source, controls: { position: 'top' } })
new Zine(el, { source, controls: { docked: false } })
new Zine(el, { source, controls: { items: ['prev', 'next', 'fullscreen'] } })
new Zine(el, { source, controls: { colorScheme: 'dark' } })
new Zine(el, { source, controls: { arrows: 'desktop' } })

Discoverability hints

A flipbook can look like a static image until someone tries a gesture. cursorHints coaches mouse users via the cursor; hints adds one-shot cues on the book for everyone (including touch). Both are on by default.

new Zine(el, { source, cursorHints: false })
new Zine(el, { source, hints: false })
new Zine(el, { source, hints: { persist: true } })

Context menu & hiding controls

Right-click opens the book's own reading controls by default. Hide named controls from every surface with hideControls, or turn the menu off with contextMenu: false (or disableContextMenu to remove the browser menu without a replacement).

Default context-menu items:

['zoomIn', 'zoomOut', '|', 'prev', 'next', '|', 'fullscreen', '|', 'print', 'download', 'share']
new Zine(el, { source, contextMenu: false })
new Zine(el, { source, contextMenu: { items: ['prev', 'next'] } })
new Zine(el, { source, hideControls: ['print', 'download'] })

Side panels

thumbnails, outline, and search open a rail beside the book. They share that space (opening one closes the others) and are as tall as the book. All three apply to documents (PDFs), not image books. Escape, the toolbar button, or a close control dismisses the panel.

  • On a wide screen, thumbnails flanks the book. The narrower outline and search rails float over the start edge so the book does not resize. Clicking the page behind a floating rail closes it.
  • Below 640px every rail is a drawer over a dimmed book. Tap the dimmed area or press Escape to close.

Styling

Left alone, controls follow the page color-scheme via CSS light-dark(). If your site has a theme toggle, set color-scheme on the themed root so the toolbar stays in sync:

html { color-scheme: light }
html.dark { color-scheme: dark }

Pin a scheme with controls.colorScheme when the host page should not decide. For a custom brand, set these properties (they win over both schemes):

.zine-controls {
  --zine-controls-bg: rgba(24, 24, 27, 0.82);
  --zine-controls-fg: #f4f4f5;
  --zine-controls-hover: rgba(255, 255, 255, 0.14);
  --zine-controls-accent: #7dd3fc;
}

Custom controls

Register with defineControl, then name the id in items. A control with children becomes a submenu. { id: 'next', title: 'Forward' } overrides only the fields you pass.

import { Zine, defineControl } from '@zinejs/core'

defineControl({
  id: 'print-host',
  title: 'Print',
  icon: '<path d="M6 9V2h12v7"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/>',
  action: ({ zine }) => window.print(),
})

new Zine(el, {
  source,
  controls: { items: ['prev', 'next', '|', 'print-host'] },
})

Zoom

zoom.enableddefault true
Whether zooming is allowed.
zoom.maxdefault 4
Maximum scale ( 1 = fit).
zoom.wheeldefault true
Ctrl/⌘ + wheel (and desktop pinch).
zoom.doubleClickdefault [1, 2, 4]
Levels cycled by double-click, or false to disable.
zoom.doubleClickInFlipZone
Whether double-click zoom works inside click-to-flip zones (default off for edge, on for half).

While zoomed, PDFs re-rasterize each visible page at the current magnification so text stays sharp up to zoom.max (whole page, debounced). Image books use their source resolution. A custom Source can honour the optional PageRequest scale hint on get.

Covers & page replace

frontCover / backCover
Image URLs prepended/appended as lone cover pages.
pages
Replace source pages by 0-based index (negative from the end).

Curl models (WebGL2)

Two models ship in the main bundle; four more are importable so you only pay for what you use.

import { Zine, ImageSource } from '@zinejs/core'
import { silk } from '@zinejs/core/curls'

new Zine(el, {
  source: new ImageSource([...urls]),
  curl: silk, // optional model, not in the default bundle
})
cone · bundled · default · anchored
Natural conical curl (PARC / iBooks-style).
simple · bundled
Flat spine rotation, no bend. Also forced automatically under prefers-reduced-motion (see Accessibility).
roll · import
Rolls into a cylinder, then unwraps onto the far side.
leaf · import · anchored
Traveling smooth-curvature wave.
flick · import · anchored
Inertial follow-through turn.
silk · import · anchored
Hand-turned S-curve with free-edge reverse curl.

Custom curls: any object matching CurlModel ( deform, anchored, optional flat / gloss). Helpers computeNormals and createPageMesh export from @zinejs/core/curls. Bundled names are also listed in @zinejs/core/options.schema.json.

Try options live in the examples.