Examples
Basic
Root holds a viewport (scroll container) and slides (data-kt-carousel-item). Add tabIndex={0} (or tabindex="0" in HTML) so ArrowLeft / ArrowRight work when the carousel is focused.
< div
class = "mx-auto w-full max-w-lg space-y-3"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
First slide
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Second slide
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Third slide
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Keyboard
Put tabindex="0" (or tabIndex={0}) on the carousel root so it can receive focus, then use ArrowLeft / ArrowRight to go to the previous or next slide while focus is inside the carousel (including on the root). The handler skips arrow keys when focus is in an input , textarea , or contenteditable node. In RTL , arrows follow visual next/previous.
< div class = "mx-auto w-full max-w-lg space-y-3" >
< p class = "text-muted-foreground text-center text-sm leading-relaxed" >
Focus the carousel (click inside it or press <!-- -->
< span class = "kt-kbd kt-kbd-xs kt-kbd-outline" >Tab</ span >), then use <!-- -->
< span class = "kt-kbd kt-kbd-xs kt-kbd-outline" >←</ span > /
< span class = "kt-kbd kt-kbd-xs kt-kbd-outline" >→</ span >
<!-- --> to move between slides. Arrow keys are ignored while typing in
inputs.
</ p >
< div
class = "rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
data-kt-carousel = "true"
tabindex = "0"
role = "region"
aria-roledescription = "carousel"
aria-label = "Keyboard carousel example"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Slide 1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Slide 2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Slide 3
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Slide 4
</ div >
</ div >
< div class = "flex justify-center gap-2 pt-3" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
</ div >
By default the viewport and thumbnail strip use hidden native scrollbars (scrolling still works). Set data-kt-carousel-show-scrollbar="true" on the root—or pass showScrollbar: true to getOrCreateInstance—to show them. This example also adds kt-scrollable on the viewport for KtUI’s thin scrollbar styling.
Overlay arrows
Add prev/next buttons as overlay controls inside a relative carousel wrapper so they stay visible and easy to click.
< div
class = "mx-auto w-full max-w-lg"
data-kt-carousel = "true"
data-kt-carousel-infinite-loop = "true"
tabindex = "0"
>
< div class = "relative overflow-hidden rounded-xl border border-border" >
< div
data-kt-carousel-viewport = "true"
class = "bg-muted flex overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "flex min-w-full shrink-0 items-center justify-center py-16 text-sm font-medium"
>
First slide
</ div >
< div
data-kt-carousel-item = "true"
class = "flex min-w-full shrink-0 items-center justify-center py-16 text-sm font-medium"
>
Second slide
</ div >
< div
data-kt-carousel-item = "true"
class = "flex min-w-full shrink-0 items-center justify-center py-16 text-sm font-medium"
>
Third slide
</ div >
</ div >
< button
type = "button"
class = "absolute top-1/2 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-xs transition-all duration-200 hover:scale-105 hover:border-primary/40 hover:bg-primary/10 hover:text-primary hover:shadow-sm focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
style = "left: 0.75rem; cursor: pointer"
data-kt-carousel-prev = "true"
aria-label = "Previous slide"
>
< svg
xmlns = "http://www.w3.org/2000/svg"
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
stroke = "currentColor"
stroke-width = "2"
stroke-linecap = "round"
stroke-linejoin = "round"
class = "lucide lucide-chevron-left size-5"
aria-hidden = "true"
>
< path d = "m15 18-6-6 6-6" ></ path >
</ svg ></ button
>< button
type = "button"
class = "absolute top-1/2 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-xs transition-all duration-200 hover:scale-105 hover:border-primary/40 hover:bg-primary/10 hover:text-primary hover:shadow-sm focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
style = "right: 0.75rem; cursor: pointer"
data-kt-carousel-next = "true"
aria-label = "Next slide"
>
< svg
xmlns = "http://www.w3.org/2000/svg"
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
stroke = "currentColor"
stroke-width = "2"
stroke-linecap = "round"
stroke-linejoin = "round"
class = "lucide lucide-chevron-right size-5"
aria-hidden = "true"
>
< path d = "m9 18 6-6-6-6" ></ path >
</ svg >
</ button >
</ div >
</ div >
Place data-kt-carousel-pagination on a wrapper; each dot is data-kt-carousel-pagination-item in slide order . Active item gets aria-current="true".
Autoplay
Autoplay respects prefers-reduced-motion: reduce (timer is not started). Hover and focus-in pause autoplay when data-kt-carousel-pause-on-hover is true (default).
< div
class = "mx-auto w-full max-w-lg space-y-3"
data-kt-carousel = "true"
data-kt-carousel-autoplay = "true"
data-kt-carousel-autoplay-interval = "3500"
data-kt-carousel-infinite-loop = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Auto 1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Auto 2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Auto 3
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Infinite loop
At the last slide, Next wraps to the first; at the first, Previous wraps to the last.
< div
class = "mx-auto w-full max-w-lg space-y-3"
data-kt-carousel = "true"
data-kt-carousel-infinite-loop = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Loop A
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Loop B
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Loop C
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
RTL
Set dir="rtl" on the root (or rely on document direction). Arrow keys follow visual next / previous in RTL.
< div
class = "mx-auto w-full max-w-lg space-y-3"
dir = "rtl"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
يمين
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
وسط
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
يسار
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
السابق</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
التالي
</ button >
</ div >
</ div >
Multiple slides
Use CSS (min-w-[45%], etc.) so several slides peek into the viewport; the component still advances one slide index per next/prev.
< div
class = "mx-auto w-full max-w-xl space-y-3"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-3 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
One
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Two
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Three
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Four
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Five
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Six
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Centered
data-kt-carousel-centered="true" scrolls the active slide with inline: "center".
< div
class = "mx-auto w-full max-w-xl space-y-3"
data-kt-carousel = "true"
data-kt-carousel-centered = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-3 overflow-x-auto scroll-smooth px-8"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
A
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
B
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
C
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
D
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
E
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[70%] min-w-[70%] shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[50%] sm:min-w-[50%]"
>
F
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Draggable
Pointer-drag on the viewport scrolls horizontally. Not combined with snap in this API: when data-kt-carousel-snap="true", draggable behavior is disabled.
< div
class = "mx-auto w-full max-w-xl space-y-3"
data-kt-carousel = "true"
data-kt-carousel-draggable = "true"
tabindex = "0"
>
< p class = "text-muted-foreground text-center text-xs" >
Drag horizontally on the track (snap is off).
</ p >
< div
data-kt-carousel-viewport = "true"
class = "flex cursor-grab gap-3 overflow-x-auto scroll-smooth active:cursor-grabbing"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 3
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 4
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 5
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 select-none items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Drag 6
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Auto height
data-kt-carousel-auto-height="true" sets the viewport height from the active slide (see ResizeObserver in implementation).
< div
class = "mx-auto w-full max-w-lg space-y-3"
data-kt-carousel = "true"
data-kt-carousel-auto-height = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-hidden overflow-y-visible scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-8 text-sm font-medium"
>
Short slide
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-20 text-sm font-medium"
>
Taller slide
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-12 text-sm font-medium"
>
Medium slide
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Snap
Add Tailwind snap-x / snap-mandatory on the viewport and snap-center on items; enable data-kt-carousel-snap="true" so drag is not attached.
< div
class = "mx-auto w-full max-w-xl space-y-3"
data-kt-carousel = "true"
data-kt-carousel-snap = "true"
tabindex = "0"
>
< p class = "text-muted-foreground text-center text-xs" >
Viewport uses scroll snap; draggable is disabled when snap is on.
</ p >
< div
data-kt-carousel-viewport = "true"
class = "flex snap-x snap-mandatory gap-3 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 3
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 4
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 5
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex w-[45%] min-w-[45%] shrink-0 snap-center items-center justify-center rounded-lg py-12 text-sm font-medium sm:w-[31%] sm:min-w-[31%]"
>
Snap 6
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Info
data-kt-carousel-current and data-kt-carousel-total show 1-based current index and slide count.
< div
class = "mx-auto w-full max-w-lg space-y-3"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Alpha
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Beta
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-16 text-sm font-medium"
>
Gamma
</ div >
</ div >
< div class = "flex items-center justify-between gap-2" >
< div class = "text-muted-foreground text-sm tabular-nums" >
< span data-kt-carousel-current = "true" class = "text-foreground font-medium"
>0</ span
>< span > / </ span >< span data-kt-carousel-total = "true" >0</ span >
</ div >
< div class = "flex gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
</ div >
Thumbnails (horizontal)
data-kt-carousel-thumbnails wraps controls; each data-kt-carousel-thumbnail maps to a slide by DOM order .
< div
class = "mx-auto w-full max-w-xl space-y-4"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-3 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T3
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T4
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T5
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
T6
</ div >
</ div >
< div
data-kt-carousel-thumbnails = "true"
class = "flex justify-center gap-2 overflow-x-auto scroll-smooth pb-1"
role = "tablist"
aria-label = "Thumbnails"
>
< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 1"
>
T1</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 2"
>
T2</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 3"
>
T3</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 4"
>
T4</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 5"
>
T5</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground shrink-0 rounded-md border px-3 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 6"
>
T6
</ button >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
Thumbnails (vertical)
Same behavior with a column layout.
< div
class = "mx-auto flex w-full max-w-2xl gap-4"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-thumbnails = "true"
class = "flex w-24 shrink-0 flex-col gap-2 overflow-y-auto scroll-smooth"
role = "tablist"
aria-label = "Thumbnails"
>
< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 1"
>
V1</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 2"
>
V2</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 3"
>
V3</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 4"
>
V4</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 5"
>
V5</ button
>< button
type = "button"
data-kt-carousel-thumbnail = "true"
class = "bg-muted hover:bg-accent border-border text-muted-foreground hover:text-foreground rounded-md border px-2 py-2 text-xs font-medium data-[kt-carousel-thumbnail-active]:border-primary data-[kt-carousel-thumbnail-active]:text-foreground"
aria-label = "Thumbnail 6"
>
V6
</ button >
</ div >
< div class = "min-w-0 flex-1 space-y-3" >
< div
data-kt-carousel-viewport = "true"
class = "flex gap-3 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V1
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V2
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V3
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V4
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V5
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
V6
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
</ div >
Destroy & reinitialize
Use KTCarousel.getInstance(root)?.dispose() then KTCarousel.getOrCreateInstance(root) after dynamic updates.
< div class = "mx-auto w-full max-w-lg space-y-4" >
< div
id = "ktui-carousel-destroy-root"
class = "space-y-3"
data-kt-carousel = "true"
tabindex = "0"
>
< div
data-kt-carousel-viewport = "true"
class = "flex gap-4 overflow-x-auto scroll-smooth"
>
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
A
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
B
</ div >
< div
data-kt-carousel-item = "true"
class = "bg-muted flex min-w-full shrink-0 items-center justify-center rounded-lg py-14 text-sm font-medium"
>
C
</ div >
</ div >
< div class = "flex justify-center gap-2" >
< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-prev = "true"
>
Previous</ button
>< button
type = "button"
class = "kt-btn kt-btn-outline kt-btn-sm"
data-kt-carousel-next = "true"
>
Next
</ button >
</ div >
</ div >
< div class = "flex flex-wrap justify-center gap-2" >
< button
type = "button"
id = "ktui-carousel-destroy-btn"
class = "kt-btn kt-btn-outline kt-btn-sm"
disabled = ""
>
Destroy carousel</ button
>< button
type = "button"
id = "ktui-carousel-reinit-btn"
class = "kt-btn kt-btn-primary kt-btn-sm"
disabled = ""
>
Reinitialize
</ button >
</ div >
< script >
( function () {
function init () {
var root = document. getElementById ( "ktui-carousel-destroy-root" );
var destroyBtn = document. getElementById ( "ktui-carousel-destroy-btn" );
var reinitBtn = document. getElementById ( "ktui-carousel-reinit-btn" );
if ( ! root || ! destroyBtn || ! reinitBtn || ! window.KTCarousel) return ;
if (destroyBtn. getAttribute ( "data-kt-carousel-destroy-demo" ) === "1" )
return ;
destroyBtn. setAttribute ( "data-kt-carousel-destroy-demo" , "1" );
function refreshButtons () {
var inst = window.KTCarousel. getInstance (root);
destroyBtn. toggleAttribute ( "disabled" , ! inst);
reinitBtn. toggleAttribute ( "disabled" , !! inst);
}
destroyBtn. addEventListener ( "click" , function () {
var inst = window.KTCarousel. getInstance (root);
if (inst) inst. dispose ();
refreshButtons ();
});
reinitBtn. addEventListener ( "click" , function () {
window.KTCarousel. getOrCreateInstance (root);
refreshButtons ();
});
/* After DOMContentLoaded: this demo's handler runs before KTUI's; defer so KTCarousel.init() has run. */
setTimeout (refreshButtons, 0 );
}
/* Inline script runs before ktui.min.js in the preview document; wait for DOM + bundle. */
function schedule () {
if (document.readyState === "loading" ) {
document. addEventListener ( "DOMContentLoaded" , init);
} else if (window.KTCarousel) {
init ();
} else {
window. addEventListener ( "load" , init);
}
}
schedule ();
})();
</ script >
</ div >
Usage
Markup
< div data-kt-carousel tabindex = "0" >
< div data-kt-carousel-viewport class = "flex gap-4 overflow-x-auto scroll-smooth" >
< div data-kt-carousel-item class = "min-w-full shrink-0" >…</ div >
< div data-kt-carousel-item class = "min-w-full shrink-0" >…</ div >
</ div >
< button type = "button" data-kt-carousel-prev >Previous</ button >
< button type = "button" data-kt-carousel-next >Next</ button >
</ div >
If you omit data-kt-carousel-viewport, the root is treated as the scroll container (only works when slides live inside the scrolling element).
Component API
Options
Name Type Default Description data-kt-carouselflag — Enables auto-init on the root. data-kt-carousel-viewportflag —
Snap vs draggable
Do not rely on drag and CSS scroll-snap together: with data-kt-carousel-snap="true", drag handlers are not attached. Use snap or drag—they are mutually exclusive in this API.
Methods
Method Description goTo(index, userInitiated?)Scrolls to slide index. next(userInitiated?) / prev(userInitiated?)Moves one slide (respects infinite). getIndex()Active slide index (0-based).
Static methods
Method Description KTCarousel.init()Initializes [data-kt-carousel] except lazy roots. KTCarousel.createInstances()Same discovery after DOM updates. KTCarousel.getInstance(element)Instance or null.
Events
Event When event.detail.payloadkt.carousel.changeActive index changes { index, prevIndex, userInitiated }
root. addEventListener ( 'kt.carousel.change' , ( e ) => {
console. log (e.detail.payload.index);
});
Accessibility
Put tabindex="0" on the root (or ensure a child is focused) so ArrowLeft / ArrowRight navigate when focus is inside the carousel.
Arrow keys are ignored while focus is in an input, textarea, or contenteditable.
Pagination and thumbnails use aria-current="true" on the active control.
Autoplay is disabled when the user prefers reduced motion.
TypeScript
import {
KTCarousel,
type KTCarouselConfigInterface,
type KTCarouselInterface,
type KTCarouselChangePayloadInterface,
} from '@keenthemes/ktui' ;
const root = document. querySelector < HTMLElement >( '[data-kt-carousel]' );
if ( ! root) throw new Error ( 'missing root' );
const carousel : KTCarouselInterface | null =
KTCarousel. getOrCreateInstance (root, {
infiniteLoop: true ,
autoplay: false ,
} satisfies KTCarouselConfigInterface );