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

@ghmm/next-carouslider

v1.4.3

Published

Custom scroll-based Slider and animation-based CarouselAnimation for React/Next.js projects

Downloads

426

Readme

Caroulslider

A zero-dependency, CSS-first slider and carousel library for React/Next.js.

  • No Tailwind required — all styles live in a single importable CSS file
  • BEM class names on every element — override anything with plain CSS, no !important needed
  • Touch & mouse drag with momentum scrolling
  • Fully accessible (ARIA roles, labels, keyboard-friendly)
  • Zero Next.js config — no transpilePackages needed, works out of the box

Installation

npm install @ghmm/next-carouslider
# or
yarn add @ghmm/next-carouslider
# or
pnpm add @ghmm/next-carouslider

Import the CSS

Import the base stylesheet once in your app (e.g. layout.tsx, _app.tsx or your global CSS):

import '@ghmm/next-carouslider/styles.css'

All layout and default visual styles live in this file. Every class uses standard CSS specificity — override anything with plain CSS, no !important needed.

Next.js App Router

Since the components use React hooks, they must run on the client. Just mark your file (or a wrapper component) with "use client" — no transpilePackages or any other config needed:

'use client'

import { Slider } from '@ghmm/next-carouslider'
import '@ghmm/next-carouslider/styles.css'

export function MySlider() {
  return (
    <Slider ariaLabel="My slider" gap={16} slidesPerView={3}>
      <div>Item 1</div>
      <div>Item 2</div>
    </Slider>
  )
}

Components

| Component | Description | |---|---| | <Slider> | Scroll-based horizontal slider with momentum drag | | <CarouselAnimation> | Animation-based carousel (steps, slide, smooth) with dots, autoplay, and more |


Slider

A performant, scroll-driven slider. Items can be dragged with mouse or touch, with momentum. Supports infinite looping, configurable gap, item width or slides-per-view.

Basic usage

import { Slider } from '@ghmm/next-carouslider'

export function MySlider() {
  return (
    <Slider ariaLabel="My slider" gap={16} slidesPerView={3}>
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
      <div>Item 4</div>
    </Slider>
  )
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | ariaLabel | string | — | Accessible label for the slider region | | gap | number | 0 | Gap in px between slides | | loopMode | 'default' \| 'infinite' | 'default' | Scroll loop behavior | | showPrevNext | boolean | false | Show prev/next arrow buttons | | itemWidth | number | — | Fixed width in px for each slide (mutually exclusive with slidesPerView) | | slidesPerView | number | — | Number of slides visible at once (mutually exclusive with itemWidth) | | className | string | — | Extra CSS class on the outer wrapper |

If neither itemWidth nor slidesPerView is set, each slide fills the full viewport width (banner mode).

Ref methods

import { useRef } from 'react'
import { Slider, SliderRef } from '@ghmm/next-carouslider'

const ref = useRef<SliderRef>(null)

ref.current?.next()           // scroll to next slide
ref.current?.prev()           // scroll to previous slide
ref.current?.goTo(2)          // scroll to slide index 2
ref.current?.getActiveIndex() // returns current active index
ref.current?.getScrollLeft()  // returns current scrollLeft value

CSS class names (BEM)

Use these classes to style the Slider from your own CSS:

| Class | Element | |---|---| | .pgx-slider | Outer wrapper (div) | | .pgx-slider__viewport | Scrollable viewport (div) | | .pgx-slider__track | Flex track holding all items (div) | | .pgx-slider__item | Individual slide wrapper (div) | | .pgx-slider__nav | Prev/next buttons container (div) | | .pgx-slider__btn | Prev and next buttons (button) | | .pgx-slider__btn--prev | Previous button | | .pgx-slider__btn--next | Next button | | .pgx-slider__btn--disabled | Button when it is disabled |

Styling examples

Style the outer wrapper

.pgx-slider {
  border-radius: 12px;
  overflow: hidden;
}

Style each slide item

.pgx-slider__item {
  border-radius: 8px;
  overflow: hidden;
}

Customize the nav buttons

.pgx-slider__btn {
  background: white !important;
  color: black !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.pgx-slider__btn--disabled {
  display: none;
}

Style with your own className

<Slider ariaLabel="Gallery" className="my-gallery-slider" gap={12} slidesPerView={2}>
  ...
</Slider>
.my-gallery-slider {
  padding: 0 24px;
}

.my-gallery-slider .pgx-slider__btn {
  background: rgba(0, 0, 0, 0.5);
  color: white;
}

CarouselAnimation

An animation-based carousel supporting multiple animation modes, dots, autoplay (step or marquee), controlled index, and infinite looping.

Basic usage

import { CarouselAnimation } from '@ghmm/next-carouslider'

export function MyCarousel() {
  return (
    <CarouselAnimation ariaLabel="My carousel" showDots showPrevNext>
      <img src="/slide1.jpg" alt="Slide 1" />
      <img src="/slide2.jpg" alt="Slide 2" />
      <img src="/slide3.jpg" alt="Slide 3" />
    </CarouselAnimation>
  )
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | ariaLabel | string | — | Accessible label for the carousel region | | activeIndex | number | — | Controlled active slide index | | onIndexChange | (index: number) => void | — | Callback when active index changes | | loopMode | 'default' \| 'loop' \| 'infinite' | 'default' | Loop behavior ('loop' wraps on next/prev, 'infinite' clones slides for seamless scroll) | | showPrevNext | boolean | false | Show prev/next arrow buttons | | showDots | boolean | false | Show pagination dots | | renderDots | (params) => ReactNode | — | Custom dot renderer (see below) | | animation | 'steps' \| 'slide' \| 'smooth' | 'steps' | Animation mode | | transitionDurationMs | number | 280 | Slide transition duration in ms | | stepZoomOutMs | number | 140 | Duration of zoom-out phase (steps mode) | | stepZoomInMs | number | 140 | Duration of zoom-in phase (steps mode) | | dragThreshold | number | 10 | Minimum drag distance in px to register a swipe | | viewportBackground | string | 'transparent' | Background of the viewport (useful for steps animation) | | align | 'start' \| 'center' \| 'end' | 'center' | Horizontal alignment of slides within the track | | gap | number | 0 | Gap in px between slides | | itemWidth | number | — | Fixed width in px per slide (strip mode) | | slidesPerView | number | — | Number of visible slides (strip mode) | | autoplay | CarouselAnimationAutoplay | — | Autoplay config (see below) | | className | string | — | Extra CSS class on the outer wrapper |

Animation modes

| Value | Description | |---|---| | 'steps' | Zoom out → slide → zoom in (default) | | 'slide' | Clean horizontal slide with easing | | 'smooth' | Slide with a slight scale effect on transition |

Autoplay config

<CarouselAnimation
  ariaLabel="Autoplay example"
  autoplay={{ mode: 'step', intervalMs: 3000 }}
>
  ...
</CarouselAnimation>
// Marquee (continuous scroll)
<CarouselAnimation
  ariaLabel="Marquee example"
  autoplay={{ mode: 'marquee', intervalMs: 0, speedPxPerSecond: 60 }}
  loopMode="infinite"
>
  ...
</CarouselAnimation>

| Field | Type | Description | |---|---|---| | mode | 'step' \| 'marquee' | Step advances one slide at a time; marquee scrolls continuously | | intervalMs | number | Interval in ms between steps | | speedPxPerSecond | number | Pixels per second for marquee mode (default: 40) |

Ref methods

import { useRef } from 'react'
import { CarouselAnimation, CarouselAnimationRef } from '@ghmm/next-carouslider'

const ref = useRef<CarouselAnimationRef>(null)

ref.current?.next()           // go to next slide
ref.current?.prev()           // go to previous slide
ref.current?.goTo(1)          // go to slide index 1
ref.current?.getActiveIndex() // returns current active index

Custom dots

<CarouselAnimation
  ariaLabel="Custom dots"
  showDots
  renderDots={({ total, activeIndex, goTo }) => (
    <div style={{ display: 'flex', gap: 8, justifyContent: 'center', marginTop: 12 }}>
      {Array.from({ length: total }, (_, i) => (
        <button
          key={i}
          onClick={() => goTo(i)}
          style={{
            width: i === activeIndex ? 24 : 8,
            height: 8,
            borderRadius: 4,
            border: 'none',
            background: i === activeIndex ? '#333' : '#ccc',
            transition: 'width 150ms ease',
            cursor: 'pointer',
          }}
        />
      ))}
    </div>
  )}
>
  ...
</CarouselAnimation>

Controlled carousel

import { useState } from 'react'
import { CarouselAnimation } from '@ghmm/next-carouslider'

export function ControlledCarousel() {
  const [index, setIndex] = useState(0)

  return (
    <>
      <CarouselAnimation
        ariaLabel="Controlled carousel"
        activeIndex={index}
        onIndexChange={setIndex}
      >
        <div>Slide A</div>
        <div>Slide B</div>
        <div>Slide C</div>
      </CarouselAnimation>

      <div style={{ display: 'flex', gap: 8, marginTop: 16, justifyContent: 'center' }}>
        {['A', 'B', 'C'].map((label, i) => (
          <button key={i} onClick={() => setIndex(i)}>
            {label}
          </button>
        ))}
      </div>
    </>
  )
}

CSS class names (BEM)

Use these classes to style the CarouselAnimation from your own CSS:

| Class | Element | |---|---| | .pgx-carousel | Outer wrapper (section) | | .pgx-carousel__viewport | Overflow-hidden viewport (div) | | .pgx-carousel__track | Transform track holding all slides (div) | | .pgx-carousel__slide | Individual slide wrapper (div) | | .pgx-carousel__slide--active | Currently active slide | | .pgx-carousel__nav | Prev/next buttons container (div) | | .pgx-carousel__btn | Prev and next buttons (button) | | .pgx-carousel__btn--prev | Previous button | | .pgx-carousel__btn--next | Next button | | .pgx-carousel__btn--disabled | Button when disabled | | .pgx-carousel__dots | Dots container (div) | | .pgx-carousel__dot | Individual dot button (button) | | .pgx-carousel__dot--active | Active dot |

CSS variables for dots

The default dots use CSS variables so you can theme them without overriding inline styles:

:root {
  --pgx-dot-color: #ccc;          /* inactive dot color */
  --pgx-dot-active-color: #1a1a2e; /* active dot color */
}

Or scope them to your carousel:

.my-carousel {
  --pgx-dot-color: rgba(255, 255, 255, 0.4);
  --pgx-dot-active-color: white;
}
<CarouselAnimation ariaLabel="Themed" className="my-carousel" showDots>
  ...
</CarouselAnimation>

Styling examples

Style the viewport (e.g. add border-radius)

.pgx-carousel__viewport {
  border-radius: 16px;
}

Style individual slides

.pgx-carousel__slide {
  padding: 0 8px;
}

Customize buttons

.pgx-carousel__btn {
  background: rgba(255, 255, 255, 0.9) !important;
  color: #333 !important;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  width: 48px !important;
  height: 48px !important;
}

.pgx-carousel__btn--disabled {
  opacity: 0 !important;
}

Use className for scoped overrides

<CarouselAnimation ariaLabel="Hero" className="hero-carousel" showPrevNext showDots>
  ...
</CarouselAnimation>
.hero-carousel .pgx-carousel__btn {
  background: white;
  color: black;
}

.hero-carousel .pgx-carousel__dots {
  margin-top: 16px;
}

.hero-carousel .pgx-carousel__dot--active {
  /* the active dot color is controlled via --pgx-dot-active-color */
}

TypeScript types

import type {
  SliderProps,
  SliderRef,
  SliderLoopMode,
  CarouselAnimationProps,
  CarouselAnimationRef,
  CarouselAnimationAutoplay,
  CarouselAnimationAutoplayMode,
  CarouselAnimationAlign,
  CarouselAnimationLoopMode,
} from '@ghmm/next-carouslider'

License

MIT