Last update:
June 24, 2025
VcSlider Component¶
The VcSlider
component is a versatile carousel/slider that displays content in a horizontal scrollable container. It's based on the Swiper.js library and provides a simple way to create carousels with customizable slides, navigation, and layout options.
Storybook¶
Basic Usage¶
<template>
<VcSlider
:slides="slides"
slidesPerView="2"
:spaceBetweenSlides="10"
:navigation="true"
>
<template #default="{slide}">
<VcImage :src="slide.imageUrl" class="tw-rounded-md" />
<div class="tw-mt-2 tw-font-medium">{{ slide.title }}</div>
<div class="tw-text-sm tw-text-[var(--neutrals-500)]">{{ slide.description }}</div>
</template>
</VcSlider>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { VcSlider, VcImage } from '@vc-shell/framework';
const slides = [
{ title: 'Slide 1', description: 'Description 1', imageUrl: 'https://picsum.photos/400/300?random=1' },
{ title: 'Slide 2', description: 'Description 2', imageUrl: 'https://picsum.photos/400/300?random=2' },
{ title: 'Slide 3', description: 'Description 3', imageUrl: 'https://picsum.photos/400/300?random=3' },
{ title: 'Slide 4', description: 'Description 4', imageUrl: 'https://picsum.photos/400/300?random=4' },
];
</script>
Props¶
Prop | Type | Default | Description |
---|---|---|---|
slides |
Record<string, unknown>[] \| unknown[] |
[] |
Array of objects representing slides to display |
navigation |
boolean |
undefined |
Enables/disables navigation buttons for the slider |
overflow |
boolean |
undefined |
Allows slides to be visible outside the container |
slidesPerView |
string \| 'auto' |
'auto' |
Number of slides visible simultaneously |
spaceBetweenSlides |
number |
10 |
Space between slides in pixels |
Slots¶
Slot | Props | Description |
---|---|---|
default |
{ slide: Object } |
Content for each slide |
prevBtn |
- | Custom previous button content |
nextBtn |
- | Custom next button content |
CSS Variables¶
The slider component uses CSS variables for theming, which can be customized:
:root {
--slider-button-background: var(--additional-50); /* Background color for navigation buttons */
--slider-button-border: var(--neutrals-300); /* Border color for navigation buttons */
--slider-button-text: var(--primary-500); /* Icon/text color for enabled navigation buttons */
--slider-button-text-disabled: var(--neutrals-400); /* Icon/text color for disabled navigation buttons */
}
Examples¶
Standard Slider with Two Visible Slides¶
<template>
<VcSlider
:slides="products"
slidesPerView="2"
:spaceBetweenSlides="10"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<VcImage :src="slide.imageUrl" class="tw-rounded-md" />
<div class="tw-mt-2 tw-font-medium">{{ slide.name }}</div>
<div class="tw-text-sm tw-text-[var(--neutrals-500)]">{{ slide.price }}</div>
</template>
</VcSlider>
</template>
<script lang="ts" setup>
import { VcSlider, VcImage } from '@vc-shell/framework';
const products = [
{ name: 'Product A', price: '$19.99', imageUrl: 'https://picsum.photos/400/300?random=1' },
{ name: 'Product B', price: '$24.99', imageUrl: 'https://picsum.photos/400/300?random=2' },
{ name: 'Product C', price: '$34.99', imageUrl: 'https://picsum.photos/400/300?random=3' },
{ name: 'Product D', price: '$44.99', imageUrl: 'https://picsum.photos/400/300?random=4' },
];
</script>
Auto-width Slides with Overflow¶
<template>
<VcSlider
:slides="categories"
slidesPerView="auto"
:overflow="true"
:spaceBetweenSlides="15"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<div class="tw-w-48">
<VcImage :src="slide.imageUrl" class="tw-rounded-md" />
<div class="tw-mt-2 tw-font-medium tw-text-center">{{ slide.name }}</div>
</div>
</template>
</VcSlider>
</template>
<script lang="ts" setup>
import { VcSlider, VcImage } from '@vc-shell/framework';
const categories = [
{ name: 'Electronics', imageUrl: 'https://picsum.photos/400/300?random=1' },
{ name: 'Clothing', imageUrl: 'https://picsum.photos/400/300?random=2' },
{ name: 'Home & Garden', imageUrl: 'https://picsum.photos/400/300?random=3' },
{ name: 'Toys', imageUrl: 'https://picsum.photos/400/300?random=4' },
{ name: 'Sports', imageUrl: 'https://picsum.photos/400/300?random=5' },
];
</script>
Single Slide with Full Width¶
<template>
<VcSlider
:slides="banners"
slidesPerView="1"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<div class="tw-relative">
<VcImage :src="slide.imageUrl" class="tw-rounded-md tw-w-full" />
<div class="tw-absolute tw-bottom-0 tw-left-0 tw-w-full tw-p-4 tw-bg-black tw-bg-opacity-50 tw-text-white">
<div class="tw-text-xl tw-font-bold">{{ slide.title }}</div>
<div class="tw-mt-1">{{ slide.description }}</div>
</div>
</div>
</template>
</VcSlider>
</template>
<script lang="ts" setup>
import { VcSlider, VcImage } from '@vc-shell/framework';
const banners = [
{
title: 'Summer Sale',
description: 'Up to 50% off on selected items',
imageUrl: 'https://picsum.photos/1200/400?random=1'
},
{
title: 'New Collection',
description: 'Check out our latest arrivals',
imageUrl: 'https://picsum.photos/1200/400?random=2'
},
{
title: 'Free Shipping',
description: 'On orders over $50',
imageUrl: 'https://picsum.photos/1200/400?random=3'
},
];
</script>
Custom Navigation Buttons¶
<template>
<VcSlider
:slides="items"
slidesPerView="2"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<VcImage :src="slide.imageUrl" class="tw-rounded-md" />
<div class="tw-mt-2 tw-font-medium">{{ slide.title }}</div>
</template>
<template #prevBtn>
<button class="tw-bg-[var(--primary-500)] tw-text-white tw-p-2 tw-rounded-full">
<VcIcon icon="material-arrow_back" size="s" />
</button>
</template>
<template #nextBtn>
<button class="tw-bg-[var(--primary-500)] tw-text-white tw-p-2 tw-rounded-full">
<VcIcon icon="material-arrow_forward" size="s" />
</button>
</template>
</VcSlider>
</template>
<script lang="ts" setup>
import { VcSlider, VcImage, VcIcon } from '@vc-shell/framework';
const items = [
{ title: 'Item 1', imageUrl: 'https://picsum.photos/400/300?random=1' },
{ title: 'Item 2', imageUrl: 'https://picsum.photos/400/300?random=2' },
{ title: 'Item 3', imageUrl: 'https://picsum.photos/400/300?random=3' },
{ title: 'Item 4', imageUrl: 'https://picsum.photos/400/300?random=4' },
];
</script>
Product Cards Carousel¶
<template>
<div class="tw-p-4">
<h2 class="tw-text-2xl tw-font-bold tw-mb-4">Featured Products</h2>
<VcSlider
:slides="products"
slidesPerView="auto"
:spaceBetweenSlides="20"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<div class="tw-w-64 tw-border tw-border-[var(--neutrals-200)] tw-rounded-lg tw-overflow-hidden">
<VcImage :src="slide.imageUrl" class="tw-w-full tw-h-48 tw-object-cover" />
<div class="tw-p-4">
<div class="tw-font-medium tw-text-lg">{{ slide.name }}</div>
<div class="tw-flex tw-justify-between tw-items-center tw-mt-2">
<div class="tw-font-bold tw-text-[var(--primary-700)]">{{ slide.price }}</div>
<VcRating :model-value="slide.rating" :max="5" variant="stars" />
</div>
<VcButton variant="primary" size="small" class="tw-mt-3 tw-w-full">
Add to Cart
</VcButton>
</div>
</div>
</template>
</VcSlider>
</div>
</template>
<script lang="ts" setup>
import { VcSlider, VcImage, VcButton, VcRating } from '@vc-shell/framework';
const products = [
{ name: 'Wireless Headphones', price: '$89.99', rating: 4.5, imageUrl: 'https://picsum.photos/400/300?random=1' },
{ name: 'Smart Watch', price: '$199.99', rating: 4.2, imageUrl: 'https://picsum.photos/400/300?random=2' },
{ name: 'Bluetooth Speaker', price: '$49.99', rating: 3.8, imageUrl: 'https://picsum.photos/400/300?random=3' },
{ name: 'Laptop Stand', price: '$29.99', rating: 4.7, imageUrl: 'https://picsum.photos/400/300?random=4' },
{ name: 'Wireless Charger', price: '$39.99', rating: 4.0, imageUrl: 'https://picsum.photos/400/300?random=5' },
];
</script>
Responsive Testimonials Slider¶
<template>
<div class="tw-bg-[var(--neutrals-100)] tw-p-6 tw-rounded-lg">
<h2 class="tw-text-2xl tw-font-bold tw-mb-4 tw-text-center">What Our Customers Say</h2>
<VcSlider
:slides="testimonials"
:slidesPerView="isMobile ? '1' : '3'"
:spaceBetweenSlides="24"
:navigation="true"
class="tw-w-full"
>
<template #default="{slide}">
<div class="tw-bg-white tw-p-5 tw-rounded-lg tw-shadow-sm tw-h-full">
<div class="tw-flex tw-items-center tw-mb-4">
<div class="tw-w-12 tw-h-12 tw-rounded-full tw-overflow-hidden tw-mr-3">
<VcImage :src="slide.avatar" class="tw-w-full tw-h-full tw-object-cover" />
</div>
<div>
<div class="tw-font-medium">{{ slide.name }}</div>
<VcRating :model-value="slide.rating" :max="5" variant="stars" />
</div>
</div>
<p class="tw-text-[var(--neutrals-700)]">{{ slide.comment }}</p>
</div>
</template>
</VcSlider>
</div>
</template>
<script lang="ts" setup>
import { inject, Ref } from 'vue';
import { VcSlider, VcImage, VcRating } from '@vc-shell/framework';
const isMobile = inject('isMobile') as Ref<boolean>;
const testimonials = [
{
name: 'John Smith',
rating: 5,
comment: 'Excellent product quality and fast shipping. Would definitely order again!',
avatar: 'https://randomuser.me/api/portraits/men/1.jpg'
},
{
name: 'Emily Johnson',
rating: 4,
comment: 'Great customer service and the product exceeded my expectations.',
avatar: 'https://randomuser.me/api/portraits/women/2.jpg'
},
{
name: 'Michael Brown',
rating: 5,
comment: 'I've been a customer for years and have never been disappointed.',
avatar: 'https://randomuser.me/api/portraits/men/3.jpg'
},
{
name: 'Sarah Wilson',
rating: 4,
comment: 'The quality is top-notch and delivery was faster than expected.',
avatar: 'https://randomuser.me/api/portraits/women/4.jpg'
},
];
</script>