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
ImageSourceorPdfSource(from@zinejs/pdf). rendererdefault'auto''auto'prefers WebGL2 and falls back to CSS. Force'webgl2'or'css', pass an ordered list, or a customRenderer.curldefault'cone'· WebGL2 only- Page-curl model. Bundled names:
'cone'(default) and'simple'. Optional models (roll,leaf,flick,silk) are imported from@zinejs/core/curlsand 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'.startPagedefault0- Zero-based page to open on. A deep-link hash takes precedence when
deepLinkis on. flipDurationdefault800ms- 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'.clickZoneSizedefault64px- Edge zone width when
clickToFlipis'edge'. clickFlipDelaydefault auto- Delay before a click flips so double-click zoom can win. Auto is
0or250when double-click zoom is active in the flip zone. cursorHintsdefaulttrue· mouse only- Change the cursor over the book to hint what a press does:
pointerwhere a click turns,zoom-inwhere double-click zooms,grab/grabbingwhen zoomed or peeling. Passfalseto keep the default cursor. No effect on touch. hintsdefaulttrue- 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.falseturns them off.{ persist: true }remembers learned gestures inlocalStorageacross visits. singlePageThresholddefault640px- Below this container width the book shows one page per spread (when
responsiveSpreadis true). responsiveSpreaddefaulttrue- Whether a narrow container may override
spreadMode. Setfalseto keep two-page spreads even on small screens. Changeable later withsetResponsiveSpread(). deepLinkdefaulttrue- Keep the current page in the URL hash (
#page=12) for shareable links and back/forward. Only thepagekey is touched. Passfalseif your app owns the hash. Only one book per page claims the hash. disableContextMenudefaultfalse- 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). contextMenudefaulttrue- Right-click menu of reading controls at the cursor (zoom, page turns, fullscreen, print, download, share). Works with
controls: falsetoo. Mouse only.falsekeeps the browser menu. Or pass{ items, colorScheme }(colorSchemefollows 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'sitems. loadingdefaulttrue- Overlay while a URL PDF downloads and the first spread prepares.
falsehides it. Drive your own from theprogressevent andready(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.
controlsdefaulttruefalsehides the toolbar. Or pass an object:controls.positiondefault'bottom''top'|'bottom'|'left'|'right'.controls.dockeddefaulttrue- Sit outside the book (sibling wrapper, container size unchanged).
falsefloats 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 withdefineControl. controls.arrowsdefaulttrue- Large page-turn arrows beside the book (not over it). On viewports under 640px they overlay the book edges instead of hiding.
falseturns 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 pagecolor-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,
thumbnailsflanks the book. The narroweroutlineandsearchrails 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
falseto 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.