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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@atomictemplate/slider

v1.0.2

Published

A fully customizable React slider/carousel with GSAP animations, marquee mode, and accessibility features

Downloads

270

Readme

@atomictemplate/slider

A fully customizable React slider/carousel with GSAP animations, lazy loading, accessibility, and marquee mode.

npm version License: MIT TypeScript

Features

  • 🖼️ Lazy Loading - IntersectionObserver-based lazy loading for optimal performance
  • ⌨️ Keyboard Navigation - Full keyboard support (Arrow keys, Home, End, PageUp, PageDown)
  • Accessibility - ARIA labels, roles, and screen reader support
  • 🎬 Media Support - Images, videos, GIFs, and Lottie animations
  • 📱 Responsive - Configurable breakpoints for different screen sizes
  • 🔄 Marquee Mode - Smooth infinite scrolling animation
  • 🎨 Render Props - Full customization via renderSlide, renderImage, renderArrow, renderDot
  • 🚀 GSAP Powered - Smooth, performant animations
  • 📦 Framework Agnostic - Works with Next.js, Vite, CRA, Remix, etc.
  • 🎯 TypeScript - Full type definitions included

Installation

# npm
npm install @atomictemplate/slider gsap

# pnpm
pnpm add @atomictemplate/slider gsap

# yarn
yarn add @atomictemplate/slider gsap

Quick Start

import { Slider } from "@atomictemplate/slider"

const slides = [
  {
    id: 1,
    media: {
      type: "image",
      src: "/images/slide1.jpg",
      alt: "Slide 1",
    },
  },
  {
    id: 2,
    media: {
      type: "image",
      src: "/images/slide2.jpg",
      alt: "Slide 2",
    },
  },
]

function App() {
  return (
    <Slider
      items={slides}
      options={{
        slidesToShow: 3,
        gap: 24,
        autoplay: { enabled: true, delay: 3000 },
        loop: true,
      }}
    />
  )
}

Usage with Next.js Image

Use the renderImage prop to integrate with Next.js Image component:

import { Slider, type ImageRenderProps } from "@atomictemplate/slider"
import Image from "next/image"

const renderNextImage = (props: ImageRenderProps) => (
  <Image
    src={props.src}
    alt={props.alt}
    width={props.width ?? 800}
    height={props.height ?? 600}
    className={props.className}
    style={{ objectFit: "cover" }}
  />
)

function App() {
  return (
    <Slider
      items={slides}
      renderImage={renderNextImage}
      options={{ slidesToShow: 2 }}
    />
  )
}

API Reference

SliderProps

| Prop | Type | Default | Description | | -------------- | --------------- | ----------------- | ----------------------------------------------- | | items | SlideItem[] | [] | Array of slide items | | children | ReactNode | - | Alternative to items - pass React children | | options | SliderOptions | DEFAULT_OPTIONS | Slider configuration | | className | string | - | Custom class for container | | style | CSSProperties | - | Custom styles for container | | renderSlide | SlideRenderer | - | Custom slide renderer | | renderImage | ImageRenderer | - | Custom image renderer (for Next.js Image, etc.) | | renderArrow | Function | - | Custom arrow renderer | | renderDot | Function | - | Custom dot renderer | | initialSlide | number | 0 | Initial slide index | | id | string | - | Unique ID for accessibility |

SliderOptions

interface SliderOptions {
  // Display
  slidesToShow?: number // Default: 1
  slidesToScroll?: number // Default: 1
  gap?: number | string // Default: 16
  centerMode?: boolean // Default: false

  // Navigation
  pagination?: "arrows" | "dots" | "both" | "none" // Default: 'both'
  arrows?: ArrowOptions
  dots?: { enabled?: boolean; className?: string }

  // Behavior
  loop?: boolean // Default: true
  speed?: number // Transition speed in ms. Default: 500
  transition?: "slide" | "fade" | "none" // Default: 'slide'
  draggable?: boolean // Default: true

  // Autoplay
  autoplay?: {
    enabled?: boolean // Default: false
    delay?: number // Default: 3000
    pauseOnHover?: boolean // Default: true
    pauseOnInteraction?: boolean // Default: true
  }

  // Marquee
  marquee?: {
    enabled?: boolean // Default: false
    speed?: number // Duration in seconds. Default: 50
    pauseOnHover?: boolean // Default: true
  }

  // Lazy Loading
  lazyLoad?: {
    enabled?: boolean // Default: true
    rootMargin?: string // Default: '200px'
    threshold?: number // Default: 0
  }

  // Accessibility
  a11y?: {
    enabled?: boolean // Default: true
    prevSlideMessage?: string
    nextSlideMessage?: string
    slideRole?: string
    containerRole?: string
  }

  // Keyboard
  keyboard?: {
    enabled?: boolean // Default: true
    onlyInViewport?: boolean // Default: true
  }

  // Responsive
  responsive?: {
    sm?: BreakpointConfig
    md?: BreakpointConfig
    lg?: BreakpointConfig
    xl?: BreakpointConfig
    "2xl"?: BreakpointConfig
  }

  // Callbacks
  onInit?: () => void
  onDestroy?: () => void
  onSlideChange?: (current: number, prev: number) => void
  onReachStart?: () => void
  onReachEnd?: () => void
}

SlideItem

interface SlideItem {
  id?: string | number
  media?: MediaSource
  content?: ReactNode
  className?: string
  onClick?: () => void
}

interface MediaSource {
  type: "image" | "video" | "gif" | "lottie"
  src: string
  alt?: string
  width?: number
  height?: number
  // Video specific
  poster?: string
  autoPlay?: boolean
  muted?: boolean
  loop?: boolean
}

SliderRef (Imperative Handle)

interface SliderRef {
  goToSlide: (index: number) => void
  nextSlide: () => void
  prevSlide: () => void
  getCurrentSlide: () => number
  getTotalSlides: () => number
  pauseAutoplay: () => void
  resumeAutoplay: () => void
}

Examples

Basic Slider

<Slider
  items={slides}
  options={{
    slidesToShow: 3,
    gap: 24,
    pagination: "both",
  }}
/>

Autoplay Slider

<Slider
  items={slides}
  options={{
    slidesToShow: 2,
    autoplay: {
      enabled: true,
      delay: 4000,
      pauseOnHover: true,
    },
    loop: true,
  }}
/>

Marquee Mode (Logo Carousel)

<Slider
  items={logoItems}
  options={{
    gap: 48,
    marquee: {
      enabled: true,
      speed: 30,
      pauseOnHover: true,
    },
  }}
/>

Responsive Configuration

<Slider
  items={slides}
  options={{
    slidesToShow: 1,
    responsive: {
      md: { slidesToShow: 2, gap: 16 },
      lg: { slidesToShow: 3, gap: 24 },
      xl: { slidesToShow: 4, gap: 32 },
    },
  }}
/>

With Ref Control

import { useRef } from "react"
import { Slider, type SliderRef } from "@atomictemplate/slider"

function App() {
  const sliderRef = useRef<SliderRef>(null)

  return (
    <>
      <Slider ref={sliderRef} items={slides} />
      <button onClick={() => sliderRef.current?.prevSlide()}>Prev</button>
      <button onClick={() => sliderRef.current?.nextSlide()}>Next</button>
    </>
  )
}

Custom Render Props

<Slider
  items={slides}
  renderSlide={({ item, index, isActive }) => (
    <div className={isActive ? "active" : ""}>
      <h3>{item.title}</h3>
      <p>{item.description}</p>
    </div>
  )}
  renderArrow={(direction, onClick, disabled) => (
    <button onClick={onClick} disabled={disabled}>
      {direction === "prev" ? "←" : "→"}
    </button>
  )}
  renderDot={(index, isActive, onClick) => (
    <button onClick={onClick} className={isActive ? "dot-active" : "dot"} />
  )}
/>

Video Slides

const videoSlides = [
  {
    id: 1,
    media: {
      type: 'video',
      src: '/videos/intro.mp4',
      poster: '/images/poster.jpg',
      autoPlay: true,
      muted: true,
      loop: true,
    },
  },
]

<Slider items={videoSlides} />

Styling

Import CSS

// Option 1: Import in your component
import '@atomictemplate/slider/styles.css'

// Option 2: Import in your global CSS
@import '@atomictemplate/slider/styles.css';

CSS Classes

The slider uses CSS Modules internally. You can override styles with your own classes:

/* Override arrow styles */
.my-slider [class*="arrow"] {
  background: #000;
  color: #fff;
}

/* Override dot styles */
.my-slider [class*="dot"] {
  width: 12px;
  height: 12px;
}

Keyboard Navigation

| Key | Action | | ------------------ | -------------- | | / ArrowLeft | Previous slide | | / ArrowRight | Next slide | | Home | First slide | | End | Last slide | | PageUp | Previous slide | | PageDown | Next slide |

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+
  • Mobile browsers (iOS Safari, Chrome Mobile)

Peer Dependencies

  • react >= 18.0.0
  • react-dom >= 18.0.0

Dependencies

  • gsap >= 3.12.0

License

MIT © Noman Jawad

Links