Carousel dengan animasi dan gestur geser, dibangun memakai Embla.
Installation
pnpm dlx kliping@latest add carouselPasang dependensi berikut:
pnpm add embla-carousel-vueSalin dan tempel kode sumber di GitHub ke project Anda.
Sesuaikan path import dengan struktur project Anda.
Usage
<script setup lang="ts">
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from '@/components/ui/carousel'
</script>
<template>
<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Examples
Ukuran
Untuk mengatur ukuran item, pakai utility class basis pada <CarouselItem />.
// 33% of the carousel width.
<template>
<Carousel>
<CarouselContent>
<CarouselItem class="basis-1/3">
...
</CarouselItem>
<CarouselItem class="basis-1/3">
...
</CarouselItem>
<CarouselItem class="basis-1/3">
...
</CarouselItem>
</CarouselContent>
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 50% on small screens and 33% on larger screens.
<template>
<Carousel>
<CarouselContent>
<CarouselItem class="md:basis-1/2 lg:basis-1/3">
...
</CarouselItem>
<CarouselItem class="md:basis-1/2 lg:basis-1/3">
...
</CarouselItem>
<CarouselItem class="md:basis-1/2 lg:basis-1/3">
...
</CarouselItem>
</CarouselContent>
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Jarak Antar Item
Untuk mengatur jarak antar item, kami memakai utility pl-[NILAI] pada <CarouselItem /> dan -ml-[NILAI] bernilai negatif pada <CarouselContent />.
Kenapa begitu: kami sempat mencoba properti gap atau layout grid pada <CarouselContent />, tapi itu menuntut perhitungan dan pikiran ekstra untuk mendapatkan jarak yang pas. Utility pl-[NILAI] dan -ml-[NILAI] ternyata jauh lebih mudah dipakai.
Anda bebas mengubahnya di project sendiri kalau memang perlu.
<template>
<Carousel>
<CarouselContent class="-ml-4">
<CarouselItem class="pl-4">
...
</CarouselItem>
<CarouselItem class="pl-4">
...
</CarouselItem>
<CarouselItem class="pl-4">
...
</CarouselItem>
</CarouselContent>
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<Carousel>
<CarouselContent class="-ml-2 md:-ml-4">
<CarouselItem class="pl-2 md:pl-4">
...
</CarouselItem>
<CarouselItem class="pl-2 md:pl-4">
...
</CarouselItem>
<CarouselItem class="pl-2 md:pl-4">
...
</CarouselItem>
</CarouselContent>
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
Orientasi
Pakai prop orientation untuk menentukan arah carousel.
<Carousel orientation="vertical | horizontal">
...
</Carousel>Opsi
Opsi bisa diteruskan ke carousel lewat prop opts. Keterangan lengkapnya ada di dokumentasi Embla Carousel.
<template>
<Carousel
:opts="{
align: 'start',
loop: true,
}"
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
API
Cara 1
Pakai emit @init-api pada komponen <Carousel /> untuk menangkap instance API-nya.
Cara 2
Bisa juga diakses lewat template ref pada komponen <Carousel />.
<script setup lang="ts">
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)
function accessApi() {
carouselContainerRef.value?.carouselApi.on('select', () => {})
}
</script>
<template>
<Carousel ref="carouselContainerRef">
...
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
Event
Event bisa didengarkan lewat API. Untuk mendapatkan instance API-nya, pakai emit @init-api pada komponen <Carousel />.
<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'
import { useCarousel } from '@/components/ui/carousel'
const api = ref<CarouselApi>()
function setApi(val: CarouselApi) {
api.value = val
}
const stop = watch(api, (api) => {
if (!api)
return
// Watch only once or use watchOnce() in @vueuse/core
nextTick(() => stop())
api.on('select', () => {
// Do something on select.
})
})
</script>
<template>
<Carousel @init-api="setApi">
...
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Keterangan lengkap soal event ada di dokumentasi Embla Carousel.
Slot Props
Slot props yang reaktif seperti carouselRef, canScrollNext..Prev, scrollNext..Prev bisa Anda ambil lewat direktif v-slot pada <Carousel v-slot="slotProps" /> untuk memperluas fungsinya.
<template>
<Carousel v-slot="{ canScrollNext, canScrollPrev }">
...
<CarouselPrevious v-if="canScrollPrev" />
<CarouselNext v-if="canScrollNext" />
</Carousel>
</template>2
3
4
5
6
7
Plugins
Pakai prop plugins untuk menambahkan plugin ke carousel.
pnpm add embla-carousel-autoplay<script setup lang="ts">
import Autoplay from 'embla-carousel-autoplay'
</script>
<template>
<Carousel
class="w-full max-w-xs"
:plugins="[Autoplay({
delay: 2000,
})]"
>
...
</Carousel>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
Keterangan lengkap soal plugin ada di dokumentasi Embla Carousel.
Requirements
Semua dipasang otomatis oleh kliping add; tabel ini untuk Anda yang memasang manual atau memeriksa project.
| Requirement | Value |
|---|---|
| Project | Vue 3.5+, Tailwind CSS v4, alias @ — disiapkan kliping init |
| npm packages | @lucide/vue@^1.31.0, @vueuse/core@^14.4.0, embla-carousel-vue@^8.6.0 |
| Kliping items | button |
| Files | components/ui/carousel/carousel.vuecomponents/ui/carousel/carousel-content.vuecomponents/ui/carousel/carousel-item.vuecomponents/ui/carousel/carousel-next.vuecomponents/ui/carousel/carousel-previous.vuecomponents/ui/carousel/index.tscomponents/ui/carousel/interface.tscomponents/ui/carousel/use-carousel.ts |
Anatomy
Susunan bagiannya seperti dipakai di contoh-contoh di atas. Impor semua bagian dari satu tempat, lalu susun begini:
<Carousel>
<CarouselContent>
<CarouselItem />
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>API Reference
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
opts? | MaybeRef<Partial<OptionsType>> | — | Embla Carousel options; axis is always derived from orientation. |
plugins? | MaybeRef<CreatePluginType<LoosePluginType, {}>[]> | — | Embla Carousel plugins, such as autoplay. |
orientation? | "vertical" | "horizontal" | "horizontal" | The layout direction. |
| Event | Payload | Description |
|---|---|---|
@init-api | [payload: EmblaCarouselType | undefined] | Emitted once Embla is ready, with its API (scroll, events, slide indexes). |
| Slot | Props |
|---|---|
#default | { canScrollNext: boolean; canScrollPrev: boolean; carouselApi: EmblaCarouselType | undefined; carouselRef: HTMLElement | undefined; orientation: "vertical" | "horizontal" | undefined; scrollNext: () => void; scrollPrev: () => void; } |
| Data attribute | Value |
|---|---|
[data-slot] | "carousel" |
CarouselContent
| Slot | Props |
|---|---|
#default | — |
| Data attribute | Value |
|---|---|
[data-slot] | "carousel-content" |
CarouselItem
| Slot | Props |
|---|---|
#default | — |
| Data attribute | Value |
|---|---|
[data-slot] | "carousel-item" |
CarouselNext
| Prop | Type | Default | Description |
|---|---|---|---|
variant? | "default" | "destructive" | "ghost" | "outline" | "secondary" | "link" | "outline" | The visual style. |
size? | "default" | "sm" | "xs" | "icon-xs" | "lg" | "icon" | "icon-sm" | "icon-lg" | "icon" | The visual size. |
| Slot | Props |
|---|---|
#default | — |
| Data attribute | Value |
|---|---|
[data-slot] | "carousel-next" |
CarouselPrevious
| Prop | Type | Default | Description |
|---|---|---|---|
variant? | "default" | "destructive" | "ghost" | "outline" | "secondary" | "link" | "outline" | The visual style. |
size? | "default" | "sm" | "xs" | "icon-xs" | "lg" | "icon" | "icon-sm" | "icon-lg" | "icon" | The visual size. |
| Slot | Props |
|---|---|
#default | — |
| Data attribute | Value |
|---|---|
[data-slot] | "carousel-previous" |
Credits
Berasal dari shadcn-vue (MIT, Copyright (c) 2023 unovue). Rantai lengkapnya di Credits.