Carousel
A transform-based carousel component. Uses CSS transforms (translateX) for smooth, performant sliding with no external dependencies.
Features
- ✅ CSS transform-based: Smooth
translateXanimations - ✅ No dependencies: Fully self-contained
- ✅ Touch/swipe gestures: Drag slides with mouse or touch
- ✅ Loop mode: Optional wrap-around at the ends (not a seamless infinite loop)
- ✅ Autoplay: With configurable delay and pause on hover/focus
- ✅ Responsive: Media query-based breakpoints
- ✅ Pagination: Dots, fraction, and progress bar
- ✅ Keyboard navigation: Arrow keys, Home, and End support
- ✅ Accessible: ARIA attributes, live region announcements, reduced motion support
Basic Example
Multiple Slides Per View
Show multiple slides at once with slidesPerView.
Pagination Dots
Add pagination dots for visual page indicators. Each bullet requires an explicit page index value (x-carousel:pagination="0", "1", etc.). The total number of navigable pages is totalSlides - slidesPerView + 1. For slidesPerView: 1 that equals the slide count. For slidesPerView: 3 over 10 slides, there are 8 navigable pages.
Pagination Fraction
Show current page position as a fraction.
Progress Bar
Display a progress bar showing carousel position.
Autoplay
Enable automatic slide progression. Use loop: true with autoplay so slides cycle continuously.
Loop Mode
Enable wrap-around at the ends with loop: true. Advancing past the last page wraps to the first, and vice versa. Note: this is a wrap, not a seamless infinite scroll (there is no clone track). The transform jumps at the boundary.
Dragging
When draggable: true (the default), the track applies user-select: none so pointer drags don't trigger text selection. As a consequence, users cannot select text inside slides while the carousel is draggable. Set draggable: false for text-heavy carousels.
While a drag is in progress, autoplay is paused. It resumes on pointer release if it was running before the drag started.
Responsive Breakpoints
Use media query strings as keys for breakpoint-specific settings.
Data Attributes
Slide Attributes
data-active- Current active slidedata-prev- Previous slidedata-next- Next slidedata-visible- Slide is visible in viewportdata-index- Slide index number
Pagination Attributes
data-active- Active pagination bullet
Example Styling
<div x-carousel:slide class="
data-active:scale-105
data-active:shadow-lg
transition-transform
">
Slide content
</div>
<button x-carousel:pagination class="
data-active:bg-blue-600
data-active:w-6
">
</button>API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
slidesPerView | number | 1 | Number of slides visible at once |
spaceBetween | number | 0 | Space between slides in pixels |
loop | boolean | false | Enable infinite looping |
keyboard | boolean | true | Enable keyboard navigation |
draggable | boolean | true | Enable mouse/touch drag |
freeMode | boolean | false | Snap to nearest slide instead of using drag direction |
threshold | number | 20 | Minimum drag distance to trigger slide change |
resistance | boolean | true | Resistance effect at edges when not looping |
autoplay | boolean | AutoplayConfig | false | Enable autoplay |
speed | number | 300 | Transition speed in ms |
easing | string | 'ease' | CSS timing function for the slide transition |
breakpoints | object | {} | Responsive breakpoint settings |
a11y | A11yConfig | see below | Accessibility configuration |
label | string | undefined | Sets aria-label on the root region |
labelledBy | string | undefined | Sets aria-labelledby on the root region (takes precedence over label) |
Autoplay Config
{
delay?: number // Delay between slides in ms (default: 3000)
pauseOnHover?: boolean // Pause on mouse hover (default: true)
pauseOnFocus?: boolean // Pause when focused (default: true)
}A11y Config
{
enabled?: boolean // Enable accessibility features (default: true)
prevSlideMessage?: string // Aria label for prev button (default: 'Previous slide')
nextSlideMessage?: string // Aria label for next button (default: 'Next slide')
}Breakpoints
{
'(min-width: 640px)': { slidesPerView: 2 },
'(min-width: 1024px)': { slidesPerView: 3, spaceBetween: 24 }
}State Properties
Access via $carousel in Alpine expressions (e.g., $carousel.activeIndex):
| Property | Type | Description |
|---|---|---|
activeIndex | number | Index of the first visible slide |
pageIndex | number | Same as activeIndex (each page is one slide position) |
totalSlides | number | Total number of slides |
totalPages | number | Number of navigable positions (totalSlides - slidesPerView + 1) |
canGoPrev | boolean | Whether previous navigation is possible |
canGoNext | boolean | Whether next navigation is possible |
progress | number | Progress percentage (0 to 100) |
isAutoplayPaused | boolean | Whether autoplay is currently paused |
Methods
| Method | Description |
|---|---|
goTo(index, smooth?, silent?) | Go to specific slide. When silent is true, suppresses the slidechange event and screen-reader announcement (used internally for resize/breakpoint reflows) |
next() | Go to next slide |
prev() | Go to previous slide |
update(settings) | Update carousel settings at runtime |
startAutoplay() | Start autoplay |
stopAutoplay() | Stop autoplay |
pauseAutoplay(source?) | Pause autoplay. source is 'hover', 'focus', or 'drag' (default 'hover'). Autoplay stays paused until every source that paused it has resumed |
resumeAutoplay(source?) | Resume autoplay from a given source (default 'hover') |
Events
| Event | Description | Payload |
|---|---|---|
slidechange | Fired when active slide changes | { index: number } |
Parts
| Part | Description |
|---|---|
viewport | Overflow container |
track | Flex container with translateX (wraps slides) |
slide | Individual slide element |
prev-button | Previous slide button |
next-button | Next slide button |
pagination | Pagination bullet (requires page index value) |
pagination-fraction | Fraction display (e.g., "1 / 3") |
pagination-progress | Progress bar element |
Slide Scope ($slide)
| Property | Type | Description |
|---|---|---|
index | number | Slide index |
isActive | boolean | Whether this is the active slide |
isPrev | boolean | Whether this is the previous slide |
isNext | boolean | Whether this is the next slide |
isVisible | boolean | Whether the slide is in the visible range |
activate() | function | Navigate to this slide |
Pagination Scope ($pagination)
| Property | Type | Description |
|---|---|---|
index | number | Page index |
isActive | boolean | Whether this is the active page |
label | string | Display label (e.g., "1") |
goTo() | function | Navigate to this page |
Accessibility
The carousel follows accessibility best practices:
- Keyboard navigation: Arrow keys to navigate, Home/End for first/last
- ARIA attributes: Proper roles and labels for screen readers
- Focus management: Keyboard focus support with visible indicators
- Reduced motion: Respects
prefers-reduced-motionsetting, and updates live when the OS setting changes - Announcements: Live region announces slide changes
- Offscreen slides: Non-visible slides get
aria-hidden="true"andinert, removing them from the tab order and screen-reader flow - RTL support: When the root has
dir="rtl", the track transform and drag direction mirror automatically. Arrow-key navigation follows the WAI-ARIA convention and does not mirror (ArrowLeft is always logical previous, ArrowRight logical next)
Keyboard Interactions
| Key | Action |
|---|---|
ArrowLeft | Go to previous slide |
ArrowRight | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Tab | Focus next interactive element |