npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

awesome-bookshelf-carousel

v0.1.6

Published

A modern React 2D Bookshelf Carousel component with smooth animations, perspective effects, and customizable navigation for showcasing books, products, or media.

Readme

Awesome Bookshelf Carousel

A modern React 2D Bookshelf Carousel component with smooth animations, perspective effects, and customizable navigation for showcasing books, products, or media.

Install

npm install awesome-bookshelf-carousel

Watch the demo here

Watch the Demo

Usage

import { AwesomeBookshelfCarousel } from 'awesome-bookshelf-carousel'
import 'awesome-bookshelf-carousel/style.css'

const books = [
  {
    id: 1,
    title: 'A Wild Sea',
    author: 'E. King',
    genre: 'Adventure',
    year: 2024,
    blurb: 'A tight story about storms and survival.',
    quote: 'The sea never bargains.',
    spineColor: '#2b5876',
    spineColorB: '#4e4376',
    texture: 'cloth',
    imageUrl: 'https://example.com/cover-1.jpg'
  }
]

export default function Demo() {
  return (
    <AwesomeBookshelfCarousel
      data={books}
      shelfHeight={260}
      spineMinWidth={44}
      spineMaxWidth={72}
      autoInterval={4200}
      onBookClick={(book) => console.log('book', book)}
      onBookChange={(index, book) => console.log('active', index, book)}
    />
  )
}

Styles

Import the stylesheet once in your app entry (or the component file) to enable layout, typography, and animations.

import 'awesome-bookshelf-carousel/style.css'

JSON Data Structure

data expects an array of BookCard objects.

[
  {
    "id": 1,
    "title": "A Wild Sea",
    "author": "E. King",
    "genre": "Adventure",
    "year": 2024,
    "blurb": "A tight story about storms and survival.",
    "quote": "The sea never bargains.",
    "spineColor": "#2b5876",
    "spineColorB": "#4e4376",
    "texture": "cloth",
    "imageUrl": "https://example.com/cover-1.jpg"
  }
]

Sample JSON Data

[
  {
    "id": 1,
    "title": "The Name of the Rose",
    "author": "Umberto Eco",
    "genre": "Historical",
    "year": 1980,
    "blurb": "A Franciscan friar investigates a series of deaths in an Italian monastery, navigating a labyrinthine library holding forbidden knowledge.",
    "quote": "Books are not made to be believed, but to be subjected to inquiry.",
    "spineColor": "#8b4513",
    "spineColorB": "#5c2c0a",
    "texture": "leather"
  },
  {
    "id": 2,
    "title": "Midnight in the Garden",
    "author": "John Berendt",
    "genre": "True Crime",
    "year": 1994,
    "blurb": "Savannah, Georgia — a city of moss-draped squares, old money, and a murder that divided polite society and fascinated the nation.",
    "quote": "The South is not a region. It is a state of mind.",
    "spineColor": "#2d5a27",
    "spineColorB": "#1a3818",
    "texture": "cloth"
  },
  {
    "id": 3,
    "title": "Invisible Cities",
    "author": "Italo Calvino",
    "genre": "Fiction",
    "year": 1972,
    "blurb": "Marco Polo describes fifty-five impossible cities to Kublai Khan. Each is a meditation on memory, desire, signs, and the nature of place itself.",
    "quote": "Cities, like dreams, are made of desires and fears.",
    "spineColor": "#4a3060",
    "spineColorB": "#2a1840",
    "texture": "linen"
  },
  {
    "id": 4,
    "title": "Gödel, Escher, Bach",
    "author": "Douglas Hofstadter",
    "genre": "Philosophy",
    "year": 1979,
    "blurb": "A towering work exploring self-reference, strange loops, and the emergent mystery of consciousness through mathematics, music and visual art.",
    "quote": "In the end, we are self-perceiving, self-inventing, locked-in mirages.",
    "spineColor": "#1a4a6e",
    "spineColorB": "#0d2a42",
    "texture": "boards"
  },
  {
    "id": 5,
    "title": "The Shadow of the Wind",
    "author": "Carlos Ruiz Zafón",
    "genre": "Mystery",
    "year": 2001,
    "blurb": "In post-war Barcelona a boy discovers a book that will define his life — and a mystery concerning its author that threatens his very existence.",
    "quote": "Every book has a soul — the soul of the person who wrote it.",
    "spineColor": "#8a2020",
    "spineColorB": "#4a1010",
    "texture": "leather"
  },
  {
    "id": 6,
    "title": "Ficciones",
    "author": "Jorge Luis Borges",
    "genre": "Short Stories",
    "year": 1944,
    "blurb": "A collection of labyrinths — the Library of Babel, the Garden of Forking Paths, Pierre Menard who rewrote Don Quixote. Each story a perfect trap.",
    "quote": "Time forks perpetually toward innumerable futures.",
    "spineColor": "#b8860b",
    "spineColorB": "#7a5800",
    "texture": "cloth"
  },
  {
    "id": 7,
    "title": "The Pillars of the Earth",
    "author": "Ken Follett",
    "genre": "Historical",
    "year": 1989,
    "blurb": "The building of a cathedral in twelfth-century England becomes an epic of love, treachery, architecture, and the slow birth of a civilisation.",
    "quote": "Great buildings, like great music, are never quite finished.",
    "spineColor": "#5c4a30",
    "spineColorB": "#3a2c18",
    "texture": "boards"
  },
  {
    "id": 8,
    "title": "One Hundred Years",
    "author": "Gabriel García Márquez",
    "genre": "Magic Realism",
    "year": 1967,
    "blurb": "Seven generations of the Buendía family in the mythical town of Macondo — a century of solitude, war, love, and forgetting.",
    "quote": "He had already understood that he would never leave that room.",
    "spineColor": "#c0682a",
    "spineColorB": "#7a3a10",
    "texture": "linen"
  }
]

Props

AwesomeBookshelfCarousel props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | data | BookCard[] | [] | Book list. If omitted and apiEndpoint is provided, data is fetched from the API. | | apiEndpoint | string | undefined | URL to fetch book data from. If set, the component fetches JSON on mount. | | apiHeaders | Record<string, string> | undefined | Optional headers merged into the fetch request. | | apiTransform | (raw: unknown) => BookCard[] | undefined | Converts API response into a BookCard[]. If omitted, the component expects an array or { data: [...] }. | | shelfHeight | number | 260 | Shelf height in pixels. | | spineMinWidth | number | 44 | Minimum book spine width in pixels. | | spineMaxWidth | number | 72 | Maximum book spine width in pixels. | | autoInterval | number | 4200 | Auto-advance interval in milliseconds. | | onBookClick | (card: BookCard) => void | undefined | Called when a book is opened/clicked. | | onBookChange | (index: number, card: BookCard) => void | undefined | Called when the active book changes. |

BookCard fields

| Field | Type | Required | Description | | --- | --- | --- | --- | | id | number | Yes | Unique id for the book. | | title | string | Yes | Book title shown on spine and open panel. | | author | string | No | Author name. | | genre | string | No | Short genre or category tag. | | year | string | number | No | Year of publication. | | blurb | string | No | Description shown when the book is open. | | quote | string | No | Pull quote or excerpt. | | spineColor | string | Yes | Primary spine color. | | spineColorB | string | No | Secondary color for gradient/texture. | | texture | 'cloth' | 'leather' | 'linen' | 'boards' | No | Spine texture. Defaults to a derived value per index if omitted. | | imageUrl | string | No | Cover image URL shown inside the open book panel. | | onClick | (card: BookCard) => void | No | Per-book click handler (overrides onBookClick). |