{"schemaVersion":"1.0","type":"Article","slug":"mantine-book-grab-any-edge-turn-any-page-ualhv","url":"https://api.zyvop.com/mantine-book-grab-any-edge-turn-any-page-ualhv","title":"Mantine Book — Grab Any Edge, Turn Any Page","subtitle":null,"tldr":"A realistic iBooks-style book for React built on Mantine: stack two-sided pages and turn them by...","keywords":["nextjs","react","TypeScript","webcomponents"],"entities":["Giovambattista Fazioli","Senior Full-stack Engineer, Lead Developer","nextjs","react","TypeScript","webcomponents","ZyVOP"],"keyTakeaways":["A realistic iBooks-style book for React built on Mantine: stack two-sided pages and turn them by dragging any point of the free edge — a pure-DOM reflection fold by default, a true 3D WebGL curl when you want it.","Introduction Most page-flip libraries cheat: you can only grab the corner, and the fold is a special-cased animation that falls apart the moment you drag somewhere unexpected.","This component started from the opposite question — what's the general math of a turning page?."],"headings":["Introduction","What is Mantine Book?","✨ Key Features","Compound API or data-driven pages","Grab anywhere, drag anywhere","Two renderers: flat and rounded","Controlled navigation with face-based indices","Riffle through big jumps","Hard covers","Reveal layers","Accessible by default","🚀 Getting Started","Props &amp; API","🎨 Styling","⚡ Performance","Live Demo &amp; Documentation","Links"],"outboundLinks":["https://gfazioli.github.io/mantine-book/","https://www.npmjs.com/package/@gfazioli/mantine-book","https://github.com/gfazioli/mantine-book","https://mantine-extensions.vercel.app/"],"contentText":"A realistic iBooks-style book for React built on Mantine: stack two-sided pages and turn them by dragging any point of the free edge — a pure-DOM reflection fold by default, a true 3D WebGL curl when you want it. Introduction Most page-flip libraries cheat: you can only grab the corner, and the fold is a special-cased animation that falls apart the moment you drag somewhere unexpected. This component started from the opposite question — what's the general math of a turning page? The answer is a perpendicular-bisector reflection fold: the grabbed point folds exactly onto your pointer, and the crease is computed from that single constraint. One code path covers every grab point and every drag direction — corner, mid-edge, upward, downward, forward, back. The classic corner turn is just the special case. What is Mantine Book? @gfazioli/mantine-book renders a realistic, interactive book: a stack of two-sided pages you turn like real paper. Drag the right half to go forward and the left half to go back, or drive it programmatically through a controlled page state — arrows, dots, sliders, keyboard, anything. It ships two rendering paths. The default flat variant is a pure DOM + CSS reflection fold — SSR-safe, zero canvas, interactive from the first paint. The opt-in rounded variant draws a true 3D curl on a WebGL canvas, with the page wrapping around the crease and a soft specular ridge along the bend. If WebGL is unavailable or a face snapshot fails, rounded degrades to flat automatically — opting in is always safe. Any JSX goes inside a page face — text, images, MDX, even live components. Faces are rendered once by React (no innerHTML cloning), so the event handlers inside your pages stay alive. ✨ Key Features Compound API or data-driven pages Compose pages declaratively, or hand the book an array — same result: &lt;Book width={260} height={360}&gt;&lt;Book.Page&gt; &lt;Book.Page.Front&gt;Front of page 1&lt;/Book.Page.Front&gt; &lt;Book.Page.Back&gt;Back of page 1&lt;/Book.Page.Back&gt;&lt;/Book.Page&gt;&lt;/Book&gt; Enter fullscreen mode Exit fullscreen mode const pages: BookPageData[] = [{ front: &lt;Cover /&gt;, back: &lt;TableOfContents /&gt; },{ front: &lt;Chapter n={1} /&gt;, back: &lt;Chapter n={2} /&gt; },]; &lt;Book width={260} height={360} pages={pages} /&gt; Enter fullscreen mode Exit fullscreen mode Grab anywhere, drag anywhere The fold is a perpendicular-bisector reflection: the crease is derived from the segment between the grab point and the pointer, so the page follows your finger exactly — from any point of the free edge, in any direction. Releases settle naturally: past flipThreshold (or with a quick side-aware swipe) the turn completes; otherwise the page snaps back. Two renderers: flat and rounded // Pure DOM reflection fold — the default, SSR-safe&lt;Book variant=\"flat\" /* ... */ /&gt; // True 3D WebGL curl with a lit specular ridge&lt;Book variant=\"rounded\" curlRadius={90} /* ... */ /&gt; Enter fullscreen mode Exit fullscreen mode The WebGL layer is lazy: it loads only when a rounded curl actually mounts, so flat users pay nothing. The whole book shares a single pooled WebGL context, no matter how many pages it has. Controlled navigation with face-based indices Page i has its front at face 2i and its back at 2i + 1 — face 0 is the closed book. Programmatic turns animate like a real drag: const [page, setPage] = useState(0); &lt;Book page={page} onPageChange={setPage} pages={pages} /&gt;&lt;Button onClick={() =&gt; setPage((p) =&gt; p + 2)}&gt;Next spread&lt;/Button&gt; Enter fullscreen mode Exit fullscreen mode Riffle through big jumps Jumping several pages at once doesn't teleport — the book riffles through every page in between, with the total time budget compressed into riffleDuration (default 1000ms) and turns queued one page in flight at a time. Wire it to a slider and scrub through a 100-page book. Hard covers &lt;Book withCover pages={pages} /&gt; Enter fullscreen mode Exit fullscreen mode With withCover, the first and last pages turn rigid around the spine — no curl, like the board of a hardcover — and the closed book is compact: centered on the play-zone, sliding into the two-page spread as the cover opens (the slide respects prefers-reduced-motion). Reveal layers revealBackground on the Book paints the inside cover — visible where no page rests and in the area a turning page uncovers. The same prop on a single Book.Page adds a side-aware layer under that page only. Accessible by default Keyboard turns (arrow keys, Home, End), a polite screen-reader live region announcing the visible pages (customizable via pageAnnouncement), and reduced-motion support throughout. 🚀 Getting Started yarn add @gfazioli/mantine-book # or npm install @gfazioli/mantine-book Enter fullscreen mode Exit fullscreen mode import '@gfazioli/mantine-book/styles.css';import { Book } from '@gfazioli/mantine-book'; export function Demo() {return ( &lt;Book width={260} height={360}&gt; &lt;Book.Page&gt; &lt;Book.Page.Front&gt;Hello&lt;/Book.Page.Front&gt; &lt;Book.Page.Back&gt;World&lt;/Book.Page.Back&gt; &lt;/Book.Page&gt; &lt;/Book&gt;);} Enter fullscreen mode Exit fullscreen mode That's a working, draggable book. The play-zone is twice the page width: the sheet rests in the right half and sweeps left as it turns. Props &amp; API The essentials, in context: &lt;Bookvariant=\"rounded\" // 'flat' (default) | 'rounded'width={300} // page width in px (default 300)height={600} // page height in px (default 600)page={page} // controlled face index (or defaultPage)onPageChange={setPage} // reports the first visible face after a turnflippingTime={600} // programmatic turn duration, msriffleDuration={1000} // total budget for multi-page riffles, msturnOrigin=\"bottom\" // 'top' | 'middle' | 'bottom' — corner arc of programmatic turnswithCover // rigid first/last pages + compact closed bookrevealBackground=\"gray.2\" // inside-cover base (MantineColor or CSS color)curlRadius={90} // rounded-variant curl tightnessshadowOpacity={0.5} // fold shadow strength (0 disables)pageBackground=\"white\" // page base color behind your contentdisabled={false} // turn off dragging/&gt; Enter fullscreen mode Exit fullscreen mode Gesture tuning: flipThreshold (px to commit a release, default 50), swipeDistance / swipeTimeThreshold (quick-swipe detection, 30px / 250ms), and mobileScrollSupport (default true — vertical scrolling keeps working on touch until a horizontal gesture claims the pointer). Every visual and gesture prop set on the Book cascades to all pages via context — and any individual Book.Page can override it locally: &lt;Book variant=\"rounded\" shadowOpacity={0.3} pages={pages}&gt;{/* …except this one, which stays flat and shadowless */}&lt;Book.Page variant=\"flat\" shadowOpacity={0} /* … */ /&gt;&lt;/Book&gt; Enter fullscreen mode Exit fullscreen mode For advanced consumers, the pure page-index math (faceToTurnedPages, turnedPagesToFace) and the fold geometry types (Point, ReflectionFold, TurnOrigin) are exported too. 🎨 Styling Full Mantine Styles API on both levels. The Book exposes root and page selectors; each page exposes root, restSheet, curlSheet, shadowLayer, revealLayer and face, plus CSS variables (--curl-page-width, --curl-page-height, --curl-page-background, --curl-shadow-color, --curl-reveal-background): &lt;BookclassNames={{ root: 'my-book' }}styles={{ page: { borderRadius: 8 } }}pages={pages}/&gt; Enter fullscreen mode Exit fullscreen mode Colors accept any MantineColor (\"dark.9\", \"gray.2\") or raw CSS value — theme-aware out of the box. ⚡ Performance Quiescent at rest — no perpetual requestAnimationFrame; the rAF loop runs only during a drag or the release settle. Lazy WebGL — the rounded renderer and its snapshot dependency load on first use; an untouched book costs zero GPU contexts. One context per book — the turn queue guarantees a single page in flight, so the whole stack shares one pooled WebGL context. Memoized stack — a turn re-renders the handful of pages involved, not all N. Live Demo &amp; Documentation The documentation site has interactive configurators for every feature — variants, curl radius, covers, riffle, gestures, events — where the displayed code regenerates live from the controls you touch: → gfazioli.github.io/mantine-book Links 📦 npm: @gfazioli/mantine-book 📖 Documentation: gfazioli.github.io/mantine-book 🔗 GitHub: gfazioli/mantine-book 🦄 Mantine Extensions Hub","contentHash":"sha256:4f3b98fcdc3e3cd278ddb1c7187439e673f6bc660bdac264a0ab585de687fe3c","authorName":"Giovambattista Fazioli","authorUrl":"https://api.zyvop.com/author/giovambattista","authorSameAs":["https://gfazioli.github.io","https://github.com/gfazioli","https://x.com/gfazioli","https://www.linkedin.com/in/giovambattistafazioli/"],"category":null,"tags":["nextjs","react","TypeScript","webcomponents"],"audience":"Readers researching nextjs","tone":"Professional, senior full-stack engineer, lead developer perspective","readingTimeMinutes":6,"wordCount":1278,"faqs":null,"primaryTopic":"nextjs","publishedAt":"2026-09-04T09:27:58.397Z","updatedAt":"2026-09-04T09:27:58.397Z","canonicalUrl":"https://dev.to/undolog/mantine-book-grab-any-edge-turn-any-page-n74"}