react-three-book-coverflow
v0.1.0
Published
A looped 3D coverflow carousel for react-three-fiber — drag, scroll, idle auto-drift, and click-to-center — with a built-in procedural hardcover book. Render any 3D item.
Downloads
15
Maintainers
Readme
react-three-book-coverflow
A looped 3D coverflow carousel for react-three-fiber — drag, scroll, idle auto-drift, and click-to-center — with a built-in procedural hardcover Book. Render books, or bring your own 3D items.
- 🔁 Infinite loop — items wrap seamlessly in both directions; the seam stays off-screen.
- 🎞️ Classic coverflow — center item faces the camera, sides tilt away and recede.
- 🖱️ Rich input — drag / swipe with inertia, scroll wheel, snap-to-nearest, and click-to-center.
- 💤 Idle auto-drift — gently rotates on its own until you touch it.
- 📖 Built-in Book — a procedural hardcover with a swappable cover image + binding color.
- 🧩 Generic — pass
renderItemto lay out any 3D object. - ⚡ Next.js ready — ships the
"use client"directive; works in the App Router.
Install
Installs straight from GitHub:
npm i github:nurassyl-healthkare/react-three-book-coverflowPeer dependencies
Your app provides these (any recent versions):
npm i react react-dom three @react-three/fiber @react-three/drei| Peer | Range |
| --- | --- |
| react / react-dom | >=18 |
| three | >=0.150 |
| @react-three/fiber | >=8 |
| @react-three/drei | >=9 |
Quick start
The easiest entry point is <BookCoverflow>, which sets up its own canvas, lighting and shadow. Give its parent a height.
import { BookCoverflow } from 'react-three-book-coverflow'
const books = [
{ id: 1, coverImage: '/covers/a.jpg', coverColor: '#26262a' },
{ id: 2, coverImage: '/covers/b.jpg', coverColor: '#8f2d2d' },
{ id: 3, coverImage: '/covers/c.jpg', coverColor: '#2f4a66' },
{ id: 4, coverImage: '/covers/d.jpg', coverColor: '#3f8f80' },
{ id: 5, coverImage: '/covers/e.jpg', coverColor: '#b79b74' },
{ id: 6, coverImage: '/covers/f.jpg', coverColor: '#c98a3a' },
{ id: 7, coverImage: '/covers/g.jpg', coverColor: '#6b4a8f' },
{ id: 8, coverImage: '/covers/h.jpg', coverColor: '#4a7c59' },
]
export default function Shelf() {
return (
<div style={{ height: '100dvh' }}>
<BookCoverflow
books={books}
background="linear-gradient(135deg, #FEECAA 0%, #FFBA55 50%, #FF9900 100%)"
onFocusChange={(i) => console.log('centered book:', i)}
/>
</div>
)
}Use 7+ books so the looped wrap seam stays off-screen.
Next.js (App Router)
The package is a client component. Import it in a client component, or lazy-load it with SSR disabled to skip server rendering of WebGL:
'use client'
import dynamic from 'next/dynamic'
const BookCoverflow = dynamic(
() => import('react-three-book-coverflow').then((m) => m.BookCoverflow),
{ ssr: false },
)Bring your own items (generic)
<Coverflow> lays out anything. Render it inside your own <Canvas> and provide renderItem:
import { Canvas } from '@react-three/fiber'
import { Coverflow } from 'react-three-book-coverflow'
const items = [{ id: 'a', color: 'tomato' } /* … */]
export function Gallery() {
return (
<div style={{ height: '100dvh' }}>
<Canvas camera={{ position: [0, 0, 6], fov: 45 }}>
<ambientLight intensity={1} />
<directionalLight position={[4, 6, 5]} intensity={1.2} />
<Coverflow
items={items}
keyFor={(it) => it.id}
renderItem={(it, index, active) => (
<mesh scale={active ? 1.1 : 1}>
<boxGeometry args={[1.4, 2, 0.1]} />
<meshStandardMaterial color={it.color} />
</mesh>
)}
/>
</Canvas>
</div>
)
}renderItem receives (item, index, active) — active is true for the currently centered/focused item, handy for styling.
Components & API
<BookCoverflow>
Batteries-included book carousel (own canvas + lighting). Accepts every <Coverflow> layout/control prop (below) plus:
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| books | BookItem[] | — | { id?, coverImage?, coverColor? } per book. |
| background | string | — | CSS background for the wrapper (e.g. a gradient). |
| className / style | — | — | Applied to the wrapper <div> (fills its parent). |
| cameraPosition | [x,y,z] | [0, 0.15, 6.2] | Camera position. |
| fov | number | 45 | Camera field of view. |
| shadow | boolean | true | Soft contact shadow under the row. |
| children | ReactNode | — | Overlay content rendered over the canvas. |
<Coverflow items renderItem />
The generic in-canvas layout. Layout props:
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| items | T[] | — | Items to lay out. |
| renderItem | (item, index, active) => ReactNode | — | Renders one item. |
| keyFor | (item, index) => Key | index | Stable React key. |
| spacing | number | 1.8 | Horizontal gap between neighbors. |
| depthStep | number | 0.35 | How far each step recedes. |
| sideAngle | number | 1.3 | Max side tilt (radians). |
| tiltK | number | 1.0 | How fast the tilt saturates. |
| centerBoost | number | 0.42 | Extra scale for the centered item. |
| focusPop | number | 1.1 | Forward pop when focused. |
| focusLift | number | 0.12 | Upward lift when focused. |
| focusScale | number | 1.0 | Extra scale when focused. |
| focusEase | number | 0.15 | Focus transition smoothing. |
| focusOnClick | boolean | true | Center an item on click. |
| onFocusChange | (index \| null) => void | — | Fires when the focus changes. |
Plus all control options (also accepted directly on <BookCoverflow> / passable to the hook):
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| dragSens | number | 0.006 | Items moved per pixel dragged. |
| wheelSens | number | 0.0016 | Items moved per wheel delta. |
| posEase | number | 0.12 | Position easing per frame. |
| inertiaDamp | number | 0.9 | Fling decay per frame. |
| autoDrift | boolean | true | Idle self-rotation. |
| driftSpeed | number | 0.18 | Idle drift speed (items/sec). |
| driftDelay | number | 2500 | Idle ms before drift resumes. |
| enableDrag | boolean | true | Pointer drag. |
| enableWheel | boolean | true | Wheel / scroll. |
| clickPx | number | 6 | Movement below which a release is a click. |
<Book coverImage coverColor tilt />
The standalone procedural hardcover. Loads a texture when coverImage is set, so render it under <Suspense> (both Coverflow and BookCoverflow do this for you).
useCoverflowControls(count, options?)
The control core, if you want to build a custom layout. Must run inside a <Canvas>. Returns { position, focused, focusedRef, focus, movedRef, advance, clickPx }; call advance(dt) each frame from useFrame, then read position.current. wrapSigned(offset, count) is exported for the looped-offset math.
Tuning the feel
Every constant is a prop with sensible defaults. A few common tweaks:
<BookCoverflow
books={books}
spacing={1.6} // tighter/looser fan
sideAngle={1.1} // flatter side covers
centerBoost={0.3} // how much bigger the center is
autoDrift={false} // stop the idle rotation
driftSpeed={0.3} // or make it faster
/>Development
npm install # install deps and build (prepare)
npm run build # bundle to dist/ (ESM + .d.ts) with tsup
npm run dev # watch build
npm run typecheckBuilt with tsup; the "use client" banner is injected at build time for Next.js.
License
MIT © nurassyl-healthkare
