A lightweight vanilla JavaScript carousel library built with modern web technologies. Based on custom elements, scroll-snap, and scroll-padding.
<!-- Option 1: Using CDN (recommended for production) -->
<script src="https://unpkg.com/snap-carousel.js@latest/dist/snap-carousel.umd.min.js"></script>
<!-- Option 2: Using local file -->
<script src="path/to/snap-carousel.umd.js"></script>
<!-- Use the carousel component (auto-registered) -->
<snap-carousel displayed="1" controls nav>
<div slot="scroller">
<div>Slide 1</div>
<div>Slide 2</div>
<div>Slide 3</div>
</div>
</snap-carousel>
<!-- Or create a custom carousel using JavaScript -->
<script>
// Features are available on the global SnapCarousel object
const { createCarousel, ControlsFeature, NavFeature } = SnapCarousel;
const CustomCarousel = createCarousel(ControlsFeature, NavFeature);
customElements.define('custom-carousel', CustomCarousel);
</script>
<snap-carousel
displayed="1"
per-page="1"
nav
controls
gap="16"
padding="16"
behavior="smooth"
loop
pager
autoplay="3000"
use-pause
responsive='[{"breakpoint": "1025", "settings": {"displayed": "2"}}]'
>
</snap-carousel>
<snap-carousel
displayed="3"
per-page="2"
gap="1rem"
padding="2rem"
controls
nav
loop
>
...
</snap-carousel>
$299.99
$159.99
$399.99
$199.99
$199.99
$199.99
<snap-carousel
displayed="1"
gap="2rem"
controls
nav
autoplay="5000"
use-pause
>
...
</snap-carousel>
<snap-carousel
displayed="1"
nav
>
...
</snap-carousel>
<snap-carousel
displayed="2"
per-page="1"
gap="2rem"
controls
nav
class="property-listings"
>
...
</snap-carousel>
$850,000
📍 Downtown, City
$2,500,000
📍 Coastal Area
$675,000
📍 Suburban Area
<snap-carousel
displayed="3"
per-page="1"
gap="2rem"
controls
nav
class="comparison-table"
>
...
</snap-carousel>
$9.99/month
$19.99/month
$49.99/month
Contact Us
<snap-carousel
displayed="3"
per-page="3"
gap="1rem"
padding="0 2rem"
controls
nav
class="film-carousel"
>
...
</snap-carousel>
A thrilling space exploration journey to the edges of our solar system reveals unexpected discoveries.
Modern myths come to life in this psychological thriller that blends reality with urban folklore.
A masterful team of thieves attempts the most daring museum heist in history.
A mysterious artifact allows a historian to communicate with people from different time periods.
An unlikely friendship forms between a young girl and a wild horse in the American wilderness.
A thrilling space exploration journey to the edges of our solar system reveals unexpected discoveries.
Modern myths come to life in this psychological thriller that blends reality with urban folklore.
A masterful team of thieves attempts the most daring museum heist in history.
A mysterious artifact allows a historian to communicate with people from different time periods.
An unlikely friendship forms between a young girl and a wild horse in the American wilderness.
<snap-carousel
displayed="1"
controls
nav
pager
scrollbar
vertical
stop
class="custom-nav-carousel"
style="height: 400px;"
>
<style>
.custom-pagination {
gap: .7rem;
display: flex;
position: absolute;
bottom: 1rem;
width: 100%;
justify-content: center;
}
.custom-pagination button {
font-size: 0;
border: none;
padding: 20px 0;
background: transparent;
cursor: pointer;
}
.custom-pagination button::after {
content: '';
display: block;
width: 3rem;
height: 0.3rem;
padding: 0;
border-radius: 0;
background: var(--border);
transition: all 0.3s ease;
font-size: 0px;
transform: translateY(2px);
}
.custom-pagination [aria-current="true"]::after {
background: var(--primary);
}
.custom-pagination button:hover::after {
background: var(--primary-dark);
transform: none;
}
.custom-nav-carousel::part(next-button) {
position: absolute;
right: 1rem;
top: 50%;
transform: translateY(-50%);
}
.custom-nav-button {
border-radius: 30px;
pointer-events: auto;
background: var(--secondary);
box-shadow: var(--shadow);
color: #333;
padding: 8px;
position: absolute;
top: 50%;
transform: translateY(-50%);
border: none;
left: 1rem;
}
.custom-nav-button[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.custom-nav-button:hover {
scale: 1.1;
}
.custom-nav-button[modifier="2"] {
transform: translateY(50px);
}
.custom-nav-carousel::part(pager) {
position: absolute;
top: 1rem;
right: 1rem;
background: var(--bg);
padding: 0.5rem 1rem;
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
margin: 0;
}
.custom-nav-carousel::part(current) {
font-size: 2rem;
color: var(--text);
}
.custom-nav-carousel::part(buttons) {
margin: 0;
}
</style>
<ul slot="scroller">
<li style="position: sticky;left: 0;top: 0;">
<div style="background: var(--primary); color: #FFF; padding: 1rem; border-radius: var(--radius-md);position: absolute;width: 200px;top:2rem;left:2rem;">
This item is not counted as a slide
</div>
</li>
<li>
<div class="nav-demo-slide">
<h3>Custom Navigation Demo</h3>
<p>Showcasing different navigation styles</p>
</div>
</li>
</ul>
<div slot="pagination" class="custom-pagination">
<!-- buttons will be added here automatically -->
</div>
<!-- Change the default prev button by a custom one -->
<button slot="prev-buttons" class="custom-nav-button" direction="prev">
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M220-240v-480h80v480h-80Zm520 0L380-480l360-240v480Zm-80-240Zm0 90v-180l-136 90 136 90Z"/></svg>
</button>
<!-- Add a completely custom prev button which goes back 2 slides -->
<button slot="prev-buttons" class="custom-nav-button" direction="prev" modifier="2">Prev (x2)</button>
<!-- Change the next button icon -->
<span slot="next-icon">
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M660-240v-480h80v480h-80Zm-440 0v-480l360 240-360 240Zm80-240Zm0 90 136-90-136-90v180Z"/></svg>
</span>
<!-- Change the separator text -->
<span slot="sep">
OF
</span>
<!-- Remove the next buttton label -->
<span slot="next-label"></span>
</snap-carousel>
<!-- Using UMD bundle in browser (auto-registers the component) -->
<script src="https://unpkg.com/snap-carousel.js@latest/dist/snap-carousel.umd.js"></script>
<snap-carousel displayed="3" gap="20" controls nav>
<div slot="scroller">
<div>Slide 1</div>
<div>Slide 2</div>
<div>Slide 3</div>
</div>
</snap-carousel>
// Import from npm package
import { SnapCarousel } from 'snap-carousel.js';
// Register the web component
customElements.define('snap-carousel', SnapCarousel);
// Import base and features
import { BaseCarousel, createCarousel } from './base-carousel';
import { NavFeature } from './features/nav';
import { PagerFeature } from './features/pager';
// Create custom carousel with only navigation and pager
const CustomCarousel = createCarousel(NavFeature, PagerFeature);
customElements.define('custom-carousel', CustomCarousel);
// Use in HTML
<custom-carousel displayed="3" nav pager>
<div slot="scroller">...</div>
</custom-carousel>
// Import specific features
import { ControlsFeature } from './features/controls';
import { NavFeature } from './features/nav';
import { PagerFeature } from './features/pager';
// Create carousel with only the features you need
const MinimalCarousel = createCarousel(ControlsFeature);
const FullCarousel = createCarousel(ControlsFeature, NavFeature, PagerFeature);
// Register custom elements
customElements.define('minimal-carousel', MinimalCarousel);
customElements.define('full-carousel', FullCarousel);
// vite.config.js
export default defineConfig({
build: {
lib: {
entry: 'src/snap-carousel.js',
formats: ['es', 'umd'],
fileName: (format, entryName) => {
const formatSuffix = format === 'es' ? 'esm' : format;
return `${entryName}.${formatSuffix}.js`;
},
name: 'SnapCarousel'
}
}
});
// main.js
import { BaseCarousel, createCarousel } from './base-carousel';
import { ControlsFeature } from './features/controls';
// Tree-shaking will remove unused features
const MyCarousel = createCarousel(ControlsFeature);
customElements.define('my-carousel', MyCarousel);
// Base carousel and composition
import { BaseCarousel, createCarousel } from './base-carousel';
// Individual features
import { ControlsFeature } from './features/controls';
import { NavFeature } from './features/nav';
import { PagerFeature } from './features/pager';
// Pre-configured variants
import {
SnapCarousel, // All features
SnapCarouselNav, // Navigation only
SnapCarouselPager, // Pager only
SnapCarouselControls, // Controls only
SnapCarouselNavControls,
SnapCarouselNavPager,
SnapCarouselPagerControls
} from './snap-carousel';