Skip to content

Usage

Everything happens through three auto-imported components. You never import them — registering the module is enough.

ComponentRole
<Splide>The slider root. Takes the options prop and emits Splide events.
<SplideSlide>Wraps a single slide. Put one per item.
<SplideTrack>An optional explicit track, used when you want to place arrows, pagination, or other controls yourself.

A slider is a <Splide> containing one <SplideSlide> per item:

<template>
<Splide :options="{ rewind: true, perPage: 1 }" aria-label="My slider">
<SplideSlide>
<img src="/image-1.jpg" alt="Slide 1">
</SplideSlide>
<SplideSlide>
<img src="/image-2.jpg" alt="Slide 2">
</SplideSlide>
<SplideSlide>
<img src="/image-3.jpg" alt="Slide 3">
</SplideSlide>
</Splide>
</template>

All of the slider’s behavior comes from the options object passed to <Splide>. It accepts the full Splide options set — type, layout, autoplay, breakpoints, and more.

<template>
<Splide
:options="{
type: 'loop',
perPage: 3,
perMove: 1,
gap: '1rem',
pagination: true,
arrows: true,
}"
aria-label="Featured products"
>
<SplideSlide v-for="product in products" :key="product.id">
<img :src="product.image" :alt="product.name">
</SplideSlide>
</Splide>
</template>
<script setup lang="ts">
const products = [
{ id: 1, name: 'One', image: '/p1.jpg' },
{ id: 2, name: 'Two', image: '/p2.jpg' },
{ id: 3, name: 'Three', image: '/p3.jpg' },
]
</script>

The options prop is reactive — update the bound object and the slider re-applies the new options without remounting:

<template>
<Splide :options="options" aria-label="Responsive gallery">
<SplideSlide v-for="n in 6" :key="n">
<img :src="`/img-${n}.jpg`" :alt="`Image ${n}`">
</SplideSlide>
</Splide>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const options = ref({ perPage: 3, gap: '1rem' })
// Later — the slider updates live:
// options.value = { ...options.value, perPage: 1 }
</script>

A few of the most common options:

OptionTypeDescription
type'slide' | 'loop' | 'fade'Carousel mode. 'loop' cycles infinitely; 'fade' cross-fades single slides.
perPagenumberHow many slides are visible at once.
perMovenumberHow many slides advance per navigation.
gapstring | numberSpace between slides (e.g. '1rem').
rewindbooleanGo back to the start when reaching the end (for 'slide' type).
autoplaybooleanStart autoplay on mount.
paginationbooleanShow the pagination dots.
arrowsbooleanShow the prev/next arrows.
direction'ltr' | 'rtl' | 'ttb'Slide direction, including right-to-left and vertical ('ttb').
breakpointsobjectPer-width option overrides, e.g. { 640: { perPage: 1 } }.

See the full options reference for everything else.

Splide events are bound on <Splide> with the @splide:<event> syntax. The handler receives the Splide instance (plus event-specific arguments):

<template>
<Splide
:options="{ type: 'loop' }"
aria-label="Tracked slider"
@splide:moved="onMoved"
>
<SplideSlide v-for="n in 4" :key="n">
<img :src="`/img-${n}.jpg`" :alt="`Image ${n}`">
</SplideSlide>
</Splide>
</template>
<script setup lang="ts">
function onMoved(splide: unknown, newIndex: number, prevIndex: number) {
console.log(`Moved from slide ${prevIndex} to ${newIndex}`)
}
</script>

Every Splide event is available this way — @splide:mounted, @splide:moved, @splide:active, @splide:click, and the rest. See the Splide events list.

By default <Splide> renders its own track. To place arrows, pagination, or a progress bar exactly where you want them, set :has-track="false" and provide an explicit <SplideTrack>:

<template>
<Splide :has-track="false" :options="{ type: 'loop' }" aria-label="Custom controls">
<SplideTrack>
<SplideSlide v-for="n in 4" :key="n">
<img :src="`/img-${n}.jpg`" :alt="`Image ${n}`">
</SplideSlide>
</SplideTrack>
<div class="splide__arrows">
<button class="splide__arrow splide__arrow--prev">Prev</button>
<button class="splide__arrow splide__arrow--next">Next</button>
</div>
</Splide>
</template>