A Vue 3 (Composition API + TypeScript) port of the Slick carousel, aiming for full parity with the react-slick settings API. Same DOM structure and CSS class names as the original, so slick's stylesheets — and most slick-based custom CSS — keep working.
- All ~40 react-slick settings, reactive at runtime: infinite loop, autoplay, fade, centerMode, vertical, RTL, variableWidth, adaptiveHeight, rows/slidesPerRow grid, responsive breakpoints (with
'unslick'), lazy loading,asNavForsynced carousels, and more - Touch & mouse drag with slick's swipe thresholds, edge friction, and
swipeToSlide - Accessible: arrow-key navigation,
aria-hiddenmanagement, dots as atablist - SSR-safe (Nuxt 3): full markup server-rendered with no hydration mismatch
- Zero runtime dependencies besides
@vueuse/core;vueis a peer dependency - Ships typed: full TypeScript definitions for settings, events, and the exposed API
npm i @mihnsen/vue-slickvue (^3.4) is a peer dependency.
Import the functional stylesheet (layout: list/track/slide positioning, vertical mode, fade, pre-init state) and, optionally, the theme stylesheet (default arrow/dot styling — SVG data-URI icons, no font files):
import '@mihnsen/vue-slick/slick.css'
import '@mihnsen/vue-slick/slick-theme.css'Both are plain CSS; no preprocessor required.
<script setup>
import { SlickCarousel } from '@mihnsen/vue-slick'
import '@mihnsen/vue-slick/slick.css'
import '@mihnsen/vue-slick/slick-theme.css'
</script>
<template>
<SlickCarousel :dots="true" :infinite="true" :slides-to-show="3" :slides-to-scroll="1">
<div v-for="i in 6" :key="i">Slide {{ i }}</div>
</SlickCarousel>
</template><script setup>
import { SlickCarousel } from '@mihnsen/vue-slick'
import '@mihnsen/vue-slick/slick.css'
import '@mihnsen/vue-slick/slick-theme.css'
const settings = {
dots: true,
infinite: true,
slidesToShow: 3,
slidesToScroll: 1,
responsive: [
{ breakpoint: 768, settings: { slidesToShow: 1 } },
],
}
</script>
<template>
<SlickCarousel :settings="settings">
<div v-for="i in 6" :key="i">Slide {{ i }}</div>
</SlickCarousel>
</template>Settings can be passed as the settings object prop, as individual typed props, or both — precedence is defaults → settings object → individual props → the currently matched responsive breakpoint. A responsive entry whose settings is the string 'unslick' renders the slides as a plain, unstyled list.
Two additional props sit outside Settings: asNavFor — a template ref to another mounted SlickCarousel instance to synchronize navigation with — and currentSlide (used with v-model:currentSlide) for controlled binding of the active slide index.
| Name | Type | Default | Description |
|---|---|---|---|
accessibility |
boolean |
true |
Enables arrow-key navigation on the slider, aria-hidden on inactive/cloned slides, and role="tablist"/aria-selected on dots. |
adaptiveHeight |
boolean |
false |
Resizes .slick-list to match the height of the current slide. |
arrows |
boolean |
true |
Shows prev/next arrows (hidden automatically once slideCount <= slidesToShow). |
autoplay |
boolean |
false |
Automatically advances slides on an interval. |
autoplaySpeed |
number |
3000 |
Milliseconds between auto-advances. |
centerMode |
boolean |
false |
Centers the active slide with partial neighbor slides visible on both sides. |
centerPadding |
string |
'50px' |
CSS length reserved on each side of the list in centerMode. |
cssEase |
string |
'ease' |
CSS transition-timing-function used for the track's transform transition. |
dots |
boolean |
false |
Shows pagination dots below the slider (adds slick-dotted to the root element). |
dotsClass |
string |
'slick-dots' |
Class name applied to the dots <ul>. |
draggable |
boolean |
true |
Enables mouse-drag dragging in addition to touch. |
easing |
string |
'linear' |
Accepted for react-slick API compatibility only — has no effect. See Limitations. |
edgeFriction |
number |
0.35 |
Resistance multiplier applied to drag movement past the first/last slide in finite mode. |
fade |
boolean |
false |
Crossfades slides in place instead of sliding the track. |
focusOnSelect |
boolean |
false |
Clicking a partially visible slide navigates to it. |
infinite |
boolean |
true |
Wraps around at the ends using cloned slides. |
initialSlide |
number |
0 |
Index of the slide shown on mount. |
lazyLoad |
'ondemand' | 'progressive' | null |
null |
Enables data-lazy image loading — 'ondemand' promotes images as their slide becomes active, 'progressive' loads all of them in the background after mount. |
mobileFirst |
boolean |
false |
When true, responsive breakpoints match viewport widths >= the breakpoint instead of <=. |
pauseOnDotsHover |
boolean |
false |
Pauses autoplay while hovering the dots. |
pauseOnFocus |
boolean |
true |
Pauses autoplay while the slider has focus. |
pauseOnHover |
boolean |
true |
Pauses autoplay while hovering the slider. |
responsive |
{ breakpoint: number, settings: Partial<Settings> | 'unslick' }[] | null |
null |
Breakpoint-specific setting overrides; 'unslick' renders a plain unstyled list at that breakpoint. |
respondTo |
'window' | 'slider' | 'min' |
'window' |
Which width drives responsive breakpoint matching — the window, the slider container, or the smaller of the two. |
rows |
number |
1 |
Number of rows per slide group (grid mode); combine with slidesPerRow. |
rtl |
boolean |
false |
Right-to-left layout; also flips which arrow key navigates forward/back. |
slidesPerRow |
number |
1 |
Number of slides per row when rows > 1. |
slidesToShow |
number |
1 |
Number of slides visible at once. |
slidesToScroll |
number |
1 |
Number of slides advanced per navigation. |
speed |
number |
500 |
Transition duration in milliseconds. |
swipe |
boolean |
true |
Enables touch/pointer swipe navigation. |
swipeToSlide |
boolean |
false |
Lets a drag land on any slide (nearest-slide snap) rather than always moving by slidesToScroll. |
touchMove |
boolean |
true |
Gates the whole drag/swipe layer: false disables pointer dragging entirely (not just the visual "follow the finger" motion). Splitting that from swipe for full react-slick parity is a deferred fast-follow. |
touchThreshold |
number |
5 |
Divisor of the list width used to compute the drag distance required to trigger navigation. |
useCSS |
boolean |
true |
Enables the CSS transition on the track/slide; false makes changes apply instantly. |
useTransform |
boolean |
true |
Positions the track with transform: translate3d; false uses left/top instead (still CSS-transitioned). |
variableWidth |
boolean |
false |
Slides size to their own content width instead of an even split of the list. |
vertical |
boolean |
false |
Stacks and navigates slides vertically instead of horizontally. |
verticalSwiping |
boolean |
false |
Enables vertical-axis dragging when vertical is true. |
waitForAnimate |
boolean |
true |
Ignores new navigation requests while a transition is already in progress. |
| Event | Payload | Fires when |
|---|---|---|
init |
— | The carousel has mounted. |
re-init |
— | The matched responsive breakpoint entry changes. |
before-change |
(from: number, to: number) |
Just before the current slide changes. |
after-change |
(current: number) |
After a slide change completes. |
edge |
(direction: 'left' | 'right') |
Navigation is attempted past the first/last slide in finite mode. |
swipe |
(direction: string) |
A drag/swipe gesture completes. |
lazy-load |
(indexes: number[]) |
data-lazy images finish loading for the given slide indexes. |
lazy-load-error |
(index: number) |
A data-lazy image fails to load. |
update:currentSlide |
(index: number) |
The active slide index changes — pair with v-model:currentSlide. |
Access via a template ref on <SlickCarousel ref="slick">:
| Method | Description |
|---|---|
next() |
Advance by slidesToScroll. |
prev() |
Go back by slidesToScroll. |
goTo(index: number, dontAnimate?: boolean) |
Jump to a specific slide index. |
play() |
(Re)start autoplay. |
pause() |
Stop autoplay. |
currentSlide |
Read-only current slide index. |
goToUnsync(index) is also exposed; it exists to support asNavFor synchronization (it navigates without propagating back to a linked carousel, preventing ping-pong) and is not intended for direct use.
| Slot | Props | Purpose |
|---|---|---|
| default | — | Slide content — one child per slide (v-for works). |
prev-arrow |
{ disabled, onClick } |
Replace the prev arrow's markup. |
next-arrow |
{ disabled, onClick } |
Replace the next arrow's markup. |
dots |
{ dots: { count, current } } |
Replace the whole dots list (equivalent to react-slick's appendDots). |
custom-paging |
{ index } |
Replace an individual dot's content (equivalent to customPaging(i)). |
easing(jQuery-animation easing name in the original slick/react-slick) is accepted for API compatibility but is a documented no-op — this port drives transitions with native CSS transitions, andcssEaseis the supported control for the timing curve.useCSS: falseanduseTransform: falseare honored (see the settings table above) but are legacy escape hatches carried over from react-slick, not the recommended path.
SlickCarousel is SSR-safe and works out of the box with Nuxt 3: the full slide markup — including infinite-mode clones and arrows/dots — renders deterministically on the server using slick's own percentage-based sizing trick (track width = slideCount * 100 / slidesToShow%, each slide = 100 / totalPositions% of the track), which needs no layout measurement. All real measurement, event listeners, and VueUse browser utilities activate on the client in onMounted, so there is no window/document access during server rendering and no hydration mismatch. The slick-initialized class is added client-side only, once mounted.
npm install
npm run dev # playground: pages for basic, drag, layout modes, autoplay, synced nav
npm test # vitest (jsdom + node SSR suite)
npm run typecheck # vue-tsc
npm run build # library build to dist/ (ES + UMD + d.ts + css)