VueSlide: Build Smooth Image Sliders in Vue 3Image sliders are a common UI component for showcasing photos, product galleries, and feature highlights. VueSlide is a lightweight, flexible slider component built for Vue 3 that aims to make creating smooth, accessible, and responsive carousels simple. This article walks through why you might choose VueSlide, how it works, installation, basic usage, customization (styles, transitions, and autoplay), accessibility considerations, performance tips, and sample code for real-world patterns like lazy loading, thumbnails, and touch gestures.
Why choose VueSlide?
- Lightweight and focused: VueSlide keeps the API small and the bundle size minimal, avoiding the bloat of full-featured slider libraries.
- Vue 3 composition-friendly: Built with the Composition API in mind, making it easy to integrate into modern Vue apps.
- Smooth transitions: Hardware-accelerated CSS transforms and optimized event handling produce fluid animations.
- Responsive and touch-friendly: Works well on mobile with swipe support and configurable breakpoints.
- Accessible: Includes keyboard navigation and ARIA attributes so sliders are usable for assistive technologies.
Core concepts
VueSlide centers around three main concepts:
- Slides — the individual items (images, cards, etc.).
- Track — the container that moves to reveal different slides.
- Controls — navigation elements such as prev/next buttons, pagination, and thumbnails.
Under the hood, VueSlide uses CSS transforms (translate3d) for movement, a reactive index for active slide state, and event listeners for touch, mouse, and keyboard interactions. It exposes props and events so you can control autoplay, loop behavior, transition duration, and more.
Installation
Install via npm or yarn:
npm install vueslide # or yarn add vueslide
Import in a Vue 3 component:
import { createApp } from 'vue' import App from './App.vue' import VueSlide from 'vueslide' import 'vueslide/dist/vueslide.css' const app = createApp(App) app.use(VueSlide) app.mount('#app')
If you prefer local registration, import the components directly into your single-file component.
Basic usage
Here’s a minimal example showing a basic slider with three images:
<template> <vueslide class="my-slider" :perPage="1" :loop="true"> <slide v-for="(img, i) in images" :key="i"> <img :src="img" :alt="`Slide ${i+1}`" /> </slide> </vueslide> </template> <script setup> import { ref } from 'vue' const images = ref([ '/images/photo1.jpg', '/images/photo2.jpg', '/images/photo3.jpg' ]) </script> <style> .my-slider img { width: 100%; height: auto; display: block; } </style>
Props used above:
- perPage — slides visible per view.
- loop — whether slider wraps from end to start.
Navigation and pagination
Add prev/next buttons and pagination dots:
<template> <vueslide ref="slider" :perPage="1" :loop="true"> <slide v-for="(img, i) in images" :key="i"> <img :src="img" :alt="`Slide ${i+1}`" /> </slide> </vueslide> <div class="controls"> <button @click="prev">Prev</button> <button @click="next">Next</button> <div class="dots"> <button v-for="n in images.length" :key="n" :class="{ active: currentIndex === n-1 }" @click="goTo(n-1)" /> </div> </div> </template> <script setup> import { ref, onMounted } from 'vue' const images = ref(['/images/1.jpg','/images/2.jpg','/images/3.jpg']) const slider = ref(null) const currentIndex = ref(0) onMounted(() => { slider.value.$on('update:index', (i) => currentIndex.value = i) }) function prev() { slider.value.prev() } function next() { slider.value.next() } function goTo(i) { slider.value.goTo(i) } </script>
Note: VueSlide exposes instance methods (prev, next, goTo) and emits events like update:index and change.
Transitions and custom animations
Customize transition duration and easing using props or CSS variables. VueSlide uses translate3d, so use will-change and backface-visibility for smoother rendering.
Props:
- transitionDuration — milliseconds for slide animation.
- easing — CSS timing function.
CSS variables example:
.vueslide-track { --vueslide-duration: 400ms; --vueslide-easing: cubic-bezier(.22,.9,.38,1); }
For more complex animations (fade, zoom), layer slides with absolute positioning and animate opacity/transform. Use the slider’s mode prop (e.g., ‘slide’ or ‘fade’) if available.
Autoplay, pause on hover, and visibility
Autoplay can be enabled and configured:
Props:
- autoplay — boolean
- autoplayInterval — ms
- pauseOnHover — boolean
- pauseOnFocus — boolean
Implement pause on page visibility using the Page Visibility API to avoid unnecessary transitions when the tab is hidden.
Touch gestures and dragging
VueSlide supports touch swipe and mouse dragging. Tweak sensitivity and thresholds:
Props:
- draggable — enable mouse dragging
- swipeThreshold — pixels needed to trigger slide change
- edgeResistance — how much the track resists at edges
It handles pointer events and prevents accidental vertical page scrolling using directional detection: small vertical motion lets the page scroll, larger horizontal motion starts dragging the slider.
Accessibility (A11y)
- Keyboard navigation: left/right arrow keys move slides; focus management ensures controls are reachable.
- ARIA roles/labels: role=“region” and aria-roledescription=“carousel” help screen readers; active slide gets aria-hidden where appropriate.
- Pause controls: provide a button to stop autoplay for users who need it.
Example ARIA attributes on controls:
<div role="region" aria-roledescription="carousel" aria-label="Featured images"> <!-- slides --> <button aria-label="Previous slide">Prev</button> <button aria-label="Next slide">Next</button> </div>
Performance tips
- Use optimized images (WebP, srcset) and set width/height to avoid layout shifts.
- Lazy-load offscreen slides (native loading=“lazy” for images or IntersectionObserver).
- Use transform: translate3d for movement; avoid animating top/left.
- Debounce resize handling and avoid heavy operations during transitions.
Real-world patterns
- Thumbnail navigation
- Keep a synced thumbnail strip. Clicking a thumbnail calls goTo(index).
- Lazy loading
- Use IntersectionObserver to load images only when a slide is near the viewport.
- Responsive perPage
- Use breakpoints prop or watch window width to change perPage dynamically.
- Dynamic content
- When slides are added/removed, call a refresh method or use a key on the slider to re-render.
Example: thumbnails + lazy loading
<template> <vueslide ref="main" :perPage="1" :loop="true"> <slide v-for="(img,i) in images" :key="i"> <img :data-src="img" class="lazy" :alt="`Slide ${i+1}`" /> </slide> </vueslide> <div class="thumbs"> <img v-for="(t,i) in images" :key="i" :src="t" :class="{ active: currentIndex===i }" @click="goTo(i)" /> </div> </template> <script setup> import { ref, onMounted } from 'vue' const images = ref(['/1.webp','/2.webp','/3.webp']) const main = ref(null) const currentIndex = ref(0) onMounted(() => { main.value.$on('update:index', i => currentIndex.value = i) const obs = new IntersectionObserver(entries => { for (const e of entries) { if (e.isIntersecting) { const img = e.target img.src = img.dataset.src obs.unobserve(img) } } }, { root: null, rootMargin: '200px' }) document.querySelectorAll('img.lazy').forEach(img => obs.observe(img)) }) function goTo(i){ main.value.goTo(i) } </script>
Troubleshooting
- Jumping on first paint: ensure CSS for .vueslide-track sets transform and transitions properly and images have intrinsic dimensions.
- Touch swipe not smooth: check for passive event listeners, reduce DOM complexity, and avoid heavy JS during pointermove.
- Slides out of sync: confirm keys are stable and reactive list updates call the slider’s refresh method.
Conclusion
VueSlide gives you a modern, performant base for building image sliders in Vue 3 with sensible defaults and room for customization. By combining CSS transforms, lazy loading, accessibility features, and simple instance methods/events, you can create responsive, smooth carousels suitable for most web apps.
If you want, I can provide a ready-to-use component file with thumbnails, lazy loading, and autoplay configured.
Leave a Reply