Lightweight JavaScript Carousel Library


This lightweight JavaScript carousel library helps you build image sliders, product galleries, testimonial rotators, and card rows without React, jQuery, or Swiper. Touch swipe, autoplay, loop, arrows, dots, multiple visible slides, and keyboard navigation - all in a zero-dependency script that matches your other HCG libraries.

This page covers installation, live demos, options, API methods, React usage, and styling. Open the interactive demo page for every example in one place.

What is hcg-carousel?

hcg-carousel is a vanilla JavaScript carousel for sliding content horizontally inside a viewport. Mark up a .hcg-carousel-viewport and .hcg-carousel-track, add slide children, then call HcgCarousel('#id') or HcgCarousel.init() to initialize every .hcg-carousel element on the page. Arrows and dots are created automatically when enabled.

hcg-carousel examples: image slider with arrows and dots, autoplay carousel, and multiple visible slides with gap spacing
hcg-carousel examples: image slider with arrows and dots, autoplay carousel, and multiple visible slides with gap spacing.

Why use hcg-carousel?

Swiper and Slick are powerful but large. Bootstrap carousels require Bootstrap CSS and JS. hcg-carousel is a focused slider for plain HTML pages: one small script, sensible defaults, and no framework lock-in. See the feature list below for what it includes.

Live Demo

Swipe, use the arrows, or click a dot. Autoplay runs every 4 seconds and pauses on hover.

View the full demo page for autoplay, multi-slide, API, and data-attribute examples.

Comparison

Feature hcg-carousel Bootstrap carousel Swiper
DependenciesNoneBootstrap CSS + JSSwiper bundle
Touch swipeYesLimitedYes
Multiple visible slidesslidesToShowNoYes
AutoplayYesYesYes
Data-attribute initYesPartialYes
Vanilla JS APIHcgCarousel()jQuery-styleClass-based
Approximate sizeSmallLarge (full Bootstrap)Medium to large

Features

  • Zero dependencies - one JS file and companion CSS.
  • Pointer swipe - drag left or right to change slides.
  • Autoplay - boolean or custom interval in milliseconds.
  • Loop - wrap from last to first slide, or linear mode.
  • Arrows and dots - auto-created or use your own markup.
  • slidesToShow and gap - card rows and product grids.
  • slidesToScroll - advance multiple slides per navigation step.
  • update() / refresh() - recalculate after dynamic DOM changes.
  • Accessibility - focus pause and aria-live announcements.
  • Keyboard - ArrowLeft and ArrowRight when focused.
  • Static helpers - get(), destroy(), reset(), destroyAll().

Installation

Direct download
HTML
<link rel="stylesheet" href="hcg-carousel.css">
<script src="hcg-carousel.js"></script>
npm

Install from npm:

Command line
npm install hcg-carousel
JavaScript
import HcgCarousel from "hcg-carousel";
import "hcg-carousel/hcg-carousel.css";
CDN
HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hcg-carousel@1/hcg-carousel.css">
<script src="https://cdn.jsdelivr.net/npm/hcg-carousel@1/hcg-carousel.js"></script>

Basic Usage

After you install the files, build a three-level HTML wrapper on a root element, put your slide content inside the track, then call HcgCarousel(). Arrows and dots are created for you when those options are enabled.

Required markup (outer to inner):

  • Root - container with class hcg-carousel. Add an id or use another selector for JavaScript init.
  • Viewport - child .hcg-carousel-viewport. Clips overflow and handles swipe drag.
  • Track - child .hcg-carousel-track inside the viewport. This element moves horizontally.
  • Slides - one or more direct element children inside the track (images, cards, text, or any HTML). You do not need to add .hcg-carousel-slide yourself; the library applies it plus slide ARIA labels on init.

Minimal structure:

plain
.hcg-carousel                 <-- root (your container)
  .hcg-carousel-viewport       <-- required wrapper
    .hcg-carousel-track        <-- required wrapper
      <div> slide 1 </div>     <-- direct child = one slide
      <div> slide 2 </div>
      <div> slide 3 </div>

Optional in HTML: add your own .hcg-carousel-prev, .hcg-carousel-next, or .hcg-carousel-dots inside the root if you want custom control markup. Otherwise the library inserts them when arrows and dots are true.

HTML markup

Copy this structure and replace the slide content with your images, cards, or other markup.

HTML
<div id="gallery" class="hcg-carousel">
  <div class="hcg-carousel-viewport">
    <div class="hcg-carousel-track">
      <div><img src="photo-1.jpg" alt="Photo 1"></div>
      <div><img src="photo-2.jpg" alt="Photo 2"></div>
      <div><img src="photo-3.jpg" alt="Photo 3"></div>
    </div>
  </div>
</div>
Initialize

Call HcgCarousel() on the root selector after the DOM is ready. Default options enable loop, arrows, dots, swipe, and keyboard navigation.

JavaScript
const carousel = HcgCarousel("#gallery", {
  loop: true,
  arrows: true,
  dots: true,
});

Try the basic usage demo on the interactive demo page.

Autoplay

Set autoplay: true for the default 3 second interval, or pass a number for custom timing. Autoplay pauses on hover when pauseOnHover is true.

JavaScript
const carousel = HcgCarousel("#hero", {
  autoplay: 5000,
  pauseOnHover: true,
});

document.getElementById("pause-btn").addEventListener("click", () => {
  carousel.pause();
});

Try the autoplay demo on the interactive demo page.

Multiple Visible Slides

Show a row of cards or thumbnails with slidesToShow and gap. Use loop: false when you want arrows to stop at the ends.

JavaScript
HcgCarousel("#pricing-row", {
  slidesToShow: 3,
  gap: 16,
  loop: false,
});

Try the multiple visible slides demo on the interactive demo page.

Options Reference

Quick reference for all option types and defaults. See the Options copy-paste section below for copy-paste examples.

OptionTypeDefaultDescription
indexnumber0Starting slide index.
loopbooleantrueWrap from last slide to first.
autoplayboolean | numberfalsetrue uses interval; number sets ms.
intervalnumber3000Autoplay delay when autoplay: true.
pauseOnHoverbooleantruePause autoplay while pointer is over carousel.
pauseOnFocusbooleantruePause autoplay while focus is inside the carousel.
slidesToShownumber1Visible slides at once.
slidesToScrollnumber1Slides advanced per next() / prev().
gapnumber0Gap between slides in pixels.
durationnumber400Slide transition duration in ms.
easingstring"ease"CSS easing for slide transition.
arrowsbooleantrueShow prev/next buttons.
dotsbooleantrueShow dot navigation.
swipebooleantruePointer drag to change slides.
keyboardbooleantrueArrow keys when carousel is focused.
disabledbooleanfalseDisable all interaction.
ariaLabelstring"Carousel"aria-label on root region.
onChangefunctionnullCalled when active index changes (before transition ends).
afterChangefunctionnullCalled after the slide transition completes.

Options

Copy-paste examples for each option. See Types and defaults table Options Reference above for types and default values.

index

Starting slide index when the carousel initializes. Default: 0.

JavaScript
HcgCarousel("#slider", { index: 2 });
loop

Wrap from the last slide back to the first (and vice versa). Default: true.

JavaScript
HcgCarousel("#slider", { loop: false });
autoplay

Advance slides automatically. Pass true to use interval, or a number for a custom delay in milliseconds. Default: false.

JavaScript
HcgCarousel("#slider", { autoplay: true });

HcgCarousel("#hero", { autoplay: 4000 });
interval

Delay between autoplay steps when autoplay: true. Ignored when autoplay is a number. Default: 3000.

JavaScript
HcgCarousel("#slider", {
  autoplay: true,
  interval: 5000
});
pauseOnHover

Pause autoplay while the pointer is over the carousel. Default: true.

JavaScript
HcgCarousel("#slider", {
  autoplay: true,
  pauseOnHover: false
});
slidesToShow

Number of slides visible at once. Default: 1.

JavaScript
HcgCarousel("#cards", { slidesToShow: 3 });
gap

Space between slides in pixels when slidesToShow is greater than 1. Default: 0.

JavaScript
HcgCarousel("#cards", {
  slidesToShow: 2,
  gap: 12
});
duration

Slide transition duration in milliseconds. Default: 400.

JavaScript
HcgCarousel("#slider", { duration: 600 });
arrows

Show previous and next arrow buttons. Default: true.

JavaScript
HcgCarousel("#slider", { arrows: false });
dots

Show dot navigation below the viewport. Default: true.

JavaScript
HcgCarousel("#slider", { dots: false });
swipe

Allow pointer drag to change slides on touch and mouse. Default: true.

JavaScript
HcgCarousel("#slider", { swipe: false });
keyboard

Arrow keys move slides when the carousel root has focus. Default: true.

JavaScript
HcgCarousel("#slider", { keyboard: false });
disabled

Disable arrows, dots, swipe, keyboard, and autoplay. Default: false.

JavaScript
HcgCarousel("#slider", { disabled: true });
ariaLabel

Accessible name for the carousel region (aria-label on the root). Default: 'Carousel'.

JavaScript
HcgCarousel("#testimonials", {
  ariaLabel: "Customer testimonials"
});
onChange

Callback when the active slide index changes. Receives { index, slide, instance }. Default: null.

JavaScript
HcgCarousel("#slider", {
  onChange: ({ index, instance }) => {
    console.log("Slide", index, "of", instance.getSlideCount());
  }
});

API Reference

HcgCarousel(selector, options) returns an instance with these methods. The factory also exposes HcgCarousel.version, init(), get(), destroy(), reset(), and destroyAll().

Method / PropertyReturnsDescription
goTo(index, animate?)instanceMove to slide index. Second argument defaults to true.
next()instanceAdvance by slidesToScroll (default 1).
prev()instanceMove back by slidesToScroll.
play()instanceStart or resume the autoplay timer.
pause()instanceStop the autoplay timer.
getIndex()numberCurrent active slide index.
getSlideCount()numberTotal number of slides in the track.
update() / refresh()instanceRe-read slides from the track, rebuild dots, and remeasure.
setOption(name, value)instanceUpdate any option at runtime.
getOption(name)anyRead a current option value.
enable()instanceRe-enable interaction and autoplay.
disable()instanceDisable arrows, dots, swipe, keyboard, and autoplay.
reset()instanceReturn to the initial index and restart autoplay if enabled.
destroy()voidRemove listeners, library-created controls, and the instance reference.
elementElementThe root .hcg-carousel element.
HcgCarousel.init(root?, options?)arrayAuto-initialize every .hcg-carousel inside root (default document).
HcgCarousel.get(target)instance | nullStatic helper to retrieve an instance by element or selector.
HcgCarousel.destroy(target)booleanStatic helper to destroy an instance.
HcgCarousel.reset(target)booleanStatic helper to reset an instance.
HcgCarousel.destroyAll()voidDestroy every active instance on the page.
HcgCarousel.versionstringLibrary version string (currently '1.0.0').
goTo(index, animate?)

Move to a slide by zero-based index. Pass false as the second argument to jump without animation.

JavaScript
carousel.goTo(2);
carousel.goTo(0, false);
next() / prev()

Advance or move back by slidesToScroll slides. Respects loop when enabled.

JavaScript
carousel.next();
carousel.prev();
play() / pause()

Start or stop the autoplay timer. play() enables autoplay if it was off.

JavaScript
carousel.pause();
carousel.play();
getIndex() / getSlideCount()

Read the active slide index and total slide count. Useful in onChange handlers or custom UI.

JavaScript
const index = carousel.getIndex();
const total = carousel.getSlideCount();
console.log(index + 1, "of", total);
setOption(name, value) / getOption(name)

Update or read any option at runtime. Changing layout options such as slidesToShow or gap remeasures the track.

JavaScript
carousel.setOption("autoplay", 5000);
carousel.setOption("loop", false);
const interval = carousel.getOption("interval");
enable() / disable()

Toggle interaction at runtime. disable() also pauses autoplay and blocks arrows, dots, swipe, and keyboard.

JavaScript
carousel.disable();
carousel.enable();
reset()

Return to the initial slide index without animation and restart autoplay when it is enabled.

JavaScript
carousel.reset();

// Without storing the instance:
HcgCarousel.reset("#gallery");
destroy()

Remove event listeners, library-created arrows and dots, ARIA attributes, and the instance reference on the root element.

JavaScript
carousel.destroy();

// Without storing the instance:
HcgCarousel.destroy("#gallery");
element

Read-only reference to the root .hcg-carousel DOM element.

JavaScript
const root = carousel.element;
root.classList.add("is-active");
update() / refresh()

Re-read slide children from the track, rebuild dots when the library created them, clamp the index, and remeasure. Call after adding or removing slides in the DOM.

JavaScript
track.appendChild(newSlide);
carousel.refresh();
HcgCarousel.init(root?, options?)

Scan root (default document) for .hcg-carousel elements and create instances. Optional second argument applies the same options to every new instance.

JavaScript
HcgCarousel.init();

const widgets = HcgCarousel.init(document.getElementById("sidebar"));
Static: get(), destroy(), reset(), destroyAll(), version

Factory-level helpers and the version string exposed on HcgCarousel.version.

JavaScript
const carousel = HcgCarousel.get("#gallery");
HcgCarousel.destroy("#gallery");
HcgCarousel.reset("#gallery");
HcgCarousel.destroyAll();
console.log(HcgCarousel.version); // "1.0.0"

Callbacks

The library reports slide changes through onChange (when the index updates) and afterChange (when the CSS transition finishes). Pass them when you create the instance, or update later with setOption().

onChange

Fires when the active slide index changes (arrows, dots, swipe, keyboard, autoplay, or goTo()). Not fired when the index stays the same. Receives { index, slide, instance } where slide is the active slide element.

JavaScript
const counter = document.querySelector("#slide-counter");

HcgCarousel("#slider", {
  onChange: ({ index, slide, instance }) => {
    counter.textContent = (index + 1) + " / " + instance.getSlideCount();
    console.log("Active slide:", index, slide);
  },
});
afterChange

Fires after the slide transition completes (or immediately when duration is zero). Receives the same { index, slide, instance } object as onChange.

JavaScript
HcgCarousel("#slider", {
  afterChange: ({ index, slide }) => {
    const img = slide.querySelector("img");
    if (img && !img.src) img.src = img.dataset.src;
  },
});
Change callbacks at runtime

Replace onChange or afterChange after init with setOption().

JavaScript
const carousel = HcgCarousel("#slider");

carousel.setOption("onChange", ({ index, instance }) => {
  console.log("New handler:", index, "of", instance.getSlideCount());
});

Using with React

Create the carousel in useEffect and call destroy() on unmount. Keep slide markup in JSX inside the track.

Try the live React demo on StackBlitz

JavaScript
import { useEffect, useRef } from "react";
import HcgCarousel from "hcg-carousel";
import "hcg-carousel/hcg-carousel.css";

export const Gallery = ({ items }) => {
  const rootRef = useRef(null);

  useEffect(() => {
    const carousel = HcgCarousel(rootRef.current, { autoplay: 4000 });
    return () => carousel.destroy();
  }, [items.length]);

  return (
    <div ref={rootRef} className="hcg-carousel">
      <div className="hcg-carousel-viewport">
        <div className="hcg-carousel-track">
          {items.map((item) => (
            <div key={item.id}>{item.node}</div>
          ))}
        </div>
      </div>
    </div>
  );
};

Styling and Customization

Override default arrow and dot styles, or provide your own buttons with classes .hcg-carousel-prev and .hcg-carousel-next.

CSS
.hcg-carousel {
  --hcg-carousel-duration: 300ms;
}

.hcg-carousel-arrow {
  background: #111827;
  color: #fff;
  border-color: transparent;
}

.hcg-carousel-dot-active {
  background: #f59e0b;
}

Browser Support

Modern browsers with CSS flexbox, CSS transforms, and Pointer Events. IE11 is not supported.

  • Chrome, Edge, Firefox, Safari (current versions)
  • iOS Safari and Chrome for Android

FAQ

Does hcg-carousel have any dependencies?

No. hcg-carousel is plain vanilla JavaScript with zero dependencies. Include hcg-carousel.js and hcg-carousel.css and you are ready to go.

Can I show more than one slide at a time?

Yes. Set slidesToShow to 2, 3, or more and optionally gap for spacing between slides. Use slidesToScroll to advance multiple slides per step.

How do I enable autoplay?

Pass autoplay: true to use the default interval (3000 ms), or autoplay: 5000 for a custom interval in milliseconds. Call pause() or play() on the instance at any time.

Does it support touch swipe?

Yes. Pointer events on the viewport detect horizontal swipes. Set swipe: false to disable drag navigation.

Can I load hcg-carousel from a CDN?

Yes. Add link and script tags for hcg-carousel.css and hcg-carousel.js from jsDelivr, unpkg, or your own hosted copy, then call HcgCarousel('.selector') or HcgCarousel.init().