Synchronized Main Gallery and Thumbnail Navigation with vue-awesome-swiper 3
Installation
npm install vue-awesome-swiper@3 --save-dev
Specifying the version during installation prevents compatibility issues that comonly occur with the latest releases.
Global Setup
Register the component and styles in your entry file:
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper)
Component Template
<template>
<div class="gallery-container">
<!-- Main display area -->
<swiper ref="mainSwiper" :options="mainOptions" class="main-gallery">
<swiper-slide
v-for="(image, idx) in galleryImages"
:key="idx">
<div class="swiper-zoom-container">
<div class="image-container">
<img :src="image.src" class="gallery-img">
</div>
<span class="image-label">{{ image.label }}</span>
</div>
</swiper-slide>
<div slot="pagination" class="swiper-pagination"></div>
<div slot="button-prev" class="swiper-button-prev"></div>
<div slot="button-next" class="swiper-button-next"></div>
</swiper>
<!-- Thumbnail strip -->
<swiper ref="thumbSwiper" :options="thumbOptions" class="thumb-gallery">
<swiper-slide
v-for="(image, idx) in galleryImages"
:key="idx">
<div class="swiper-zoom-container">
<div class="thumb-container">
<img :src="image.src" class="gallery-img">
</div>
<span class="image-label">{{ image.label }}</span>
</div>
</swiper-slide>
</swiper>
</div>
</template>
Styling
<style lang="scss" scoped>
.gallery-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.main-gallery {
height: 420px;
background-color: #15243B;
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
}
.swiper-zoom-container {
display: flex;
flex-direction: column;
align-items: center;
}
.image-container {
width: 720px;
height: 380px;
.gallery-img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.image-label {
font-size: 16px;
color: #ffffff;
margin-top: 8px;
}
}
.thumb-gallery {
height: 160px;
margin-top: 16px;
border: 1px solid #3F587A;
background-color: #15243B;
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
}
.thumb-container {
width: 260px;
height: 120px;
margin-top: 8px;
.gallery-img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.image-label {
font-size: 14px;
color: #ffffff;
margin-top: 4px;
}
.swiper-slide-active {
.gallery-img {
border: 3px solid #3CD4FD;
}
}
}
</style>
Script Logic
<script>
export default {
data() {
return {
galleryImages: [
{ id: 0, src: 'https://img1.baidu.com/it/u=262666228,1686642610&fm=26&fmt=auto', label: 'Waste Material' },
{ id: 1, src: 'https://img0.baidu.com/it/u=1416201738,4279486216&fm=26&fmt=auto', label: 'Waste Material' },
{ id: 2, src: 'https://img0.baidu.com/it/u=2378821321,544368891&fm=26&fmt=auto', label: 'Waste Material' },
{ id: 3, src: 'https://img2.baidu.com/it/u=1118985261,4234949595&fm=26&fmt=auto', label: 'Waste Material' },
{ id: 4, src: 'https://img1.baidu.com/it/u=3151984749,173677495&fm=26&fmt=auto', label: 'Waste Material' },
{ id: 5, src: 'https://img2.baidu.com/it/u=3704585441,2694001779&fm=26&fmt=auto', label: 'Waste Material' }
],
mainOptions: {
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
observer: true,
observeParents: true,
grabCursor: true
},
thumbOptions: {
slidesPerView: 3,
spaceBetween: 30,
direction: 'horizontal',
centeredSlides: true,
grabCursor: true,
slideToClickedSlide: true,
watchSlidesVisibility: true
}
}
},
updated() {
this.$nextTick(() => {
const mainInstance = this.$refs.mainSwiper.swiper
const thumbInstance = this.$refs.thumbSwiper.swiper
mainInstance.controller.control = thumbInstance
thumbInstance.controller.control = mainInstance
})
}
}
</script>
Implementation Notes
Why updated Lifecycle Hook
Accessing $refs in the mounted hook returns undefined because the Swiper instance hasn't been initialized yet. The updated hook fires whenever the component re-renders, ensuring the Swiper DOM elements exist. How ever, for more reliable initialization, considre using a custom method called from a child component or with a small delay.
Accessing Swiper Instances
In vue-awesome-swiper@3, access the Swiper instance via this.$refs.mainSwiper.swiper. The newer version 4 uses this.$refs.mainSwiper.$swiper, but version 3 does not use the nested $refs structure. Verify this by logging this.$refs.mainSwiper to see its actual properties.
Two-Way Synchronization
Setting control properties on both Swiper instances establishes bidirectional communication. Clicking thumbnails updates the main gallery, and navigating the main gallery updates the thumbnail position. The slideToClickedSlide: true option ensures the thumbnail strip auto-scrolls to show the active slide.