Thumbnail strip linked to a SwiperRootPrimitive carousel; tapping a thumbnail shows that slide.
import SwiperThumbnailPrimitive from "@wikicasa-dev/components/generic/carousel/Primitive/SwiperThumbnailPrimitive";
A second, smaller Swiper that acts as the carousel's thumbnail rail: tapping a thumbnail moves the main carousel there, and the current one gets .swiper-slide-thumb-active. Put it in SwiperRootPrimitive's #thumbs slot (outside the clipped carousel box) and fill it with SwiperSlideImagePrimitive slides using lazy, so only the thumbnails on screen (plus preload-count, default 1, on each side) load. See SwiperRootPrimitive for the full composition.
swiper-options takes Swiper options for the strip itself (e.g. slidesPerView, spaceBetween, freeMode: true for free scrolling); root-classes and wrapper-classes add classes to its .swiper and .swiper-wrapper elements.
Usage
<script setup lang="ts">
import SwiperRootPrimitive from "@wikicasa-dev/components/generic/carousel/Primitive/SwiperRootPrimitive";
import SwiperPrimitive from "@wikicasa-dev/components/generic/carousel/Primitive/SwiperPrimitive";
import SwiperSlideImagePrimitive from "@wikicasa-dev/components/generic/carousel/Primitive/SwiperSlideImagePrimitive";
import SwiperThumbnailPrimitive from "@wikicasa-dev/components/generic/carousel/Primitive/SwiperThumbnailPrimitive";
const photos = ["/img/rustico-1.jpg", "/img/rustico-2.jpg", "/img/rustico-3.jpg", "/img/rustico-4.jpg", "/img/rustico-5.jpg"];
</script>
<template>
<SwiperRootPrimitive class="gallery">
<SwiperPrimitive>
<SwiperSlideImagePrimitive v-for="(src, i) in photos" :key="src" :src="src" :alt="`Rustico in Umbria, foto ${i + 1}`" :lazy="i > 0" />
</SwiperPrimitive>
<template #thumbs>
<SwiperThumbnailPrimitive :swiper-options="{ slidesPerView: 4, spaceBetween: 8, freeMode: true }">
<SwiperSlideImagePrimitive v-for="(src, i) in photos" :key="src" :src="src" :alt="`Miniatura ${i + 1}`" lazy img-classes="thumb" />
</SwiperThumbnailPrimitive>
</template>
</SwiperRootPrimitive>
</template>
<style scoped>
.gallery :deep(.thumb) { width: 80px; height: 80px; object-fit: cover; border-radius: 8px; opacity: 0.6; }
.gallery :deep(.swiper-slide-thumb-active .thumb) { opacity: 1; }
</style>
Do / Don't
- Do place it in the root's
#thumbsslot, rendered from the start (it registers with the root while the carousel is created). - Do use thumbnail-sized image URLs, not the full photos.
- Don't pass
thumbs,watchSlidesProgressorslideToClickedSlideinswiper-options; the link to the carousel sets them. - The strip's Swiper instance is exposed as
swiperand passed by@init.
API
Generated from the component's types. Do not edit by hand.
Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
dataCy |
string |
|||
rootClasses |
ClassTypeProp |
|||
wrapperClasses |
ClassTypeProp |
|||
swiperOptions |
SwiperThumbnailPrimitiveProps |
|||
preloadCount |
number |
1 |
Thumbnails to promote beyond each edge of the visible window. A rail whose next thumbnail pops in under the finger reads as broken, so this leans one slide out by default. Deliberately not Swiper's lazyPreloadPrevNext: that option drives Swiper's own loading="lazy" preloading, which these slides do not use - borrowing it would both misname the knob and switch on a second preloading pass on every slide change. |
Events
| Name | Payload |
|---|---|
init |
[Swiper] |
v-model
None.
Slots
| Name | Slot props |
|---|---|
default |
Exposed (template ref)
| Name | Type |
|---|---|
swiper |
Nullable<Swiper> |