<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
</script>
<Carousel.Root class="w-full max-w-xs">
<Carousel.Content>
{#each Array(5), i}
<Carousel.Item>
<div class="p-1">
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-4xl font-semibold">{i + 1}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> About
The carousel component is built using the Embla Carousel library.
Installation
pnpm dlx jsrepo@latest add carouselnpx jsrepo@latest add carouselyarn dlx jsrepo@latest add carouselbunx jsrepo@latest add carouselInstall embla-carousel-svelte:
pnpm add embla-carousel-svelte -Dnpm install embla-carousel-svelte -Dyarn add embla-carousel-svelte -Dbun add embla-carousel-svelte -DCopy and paste the following code into your project.
<script lang="ts">
import emblaCarouselSvelte from 'embla-carousel-svelte';
import type { HTMLAttributes } from 'svelte/elements';
import { getEmblaContext } from './context.js';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
const emblaCtx = getEmblaContext('<Carousel.Content/>');
</script>
<div
data-slot="carousel-content"
class="overflow-hidden bg-background"
use:emblaCarouselSvelte={{
options: {
container: '[data-embla-container]',
slides: '[data-embla-slide]',
...emblaCtx.options,
axis: emblaCtx.orientation === 'horizontal' ? 'x' : 'y'
},
plugins: emblaCtx.plugins
}}
onemblaInit={emblaCtx.onInit}
>
<div
bind:this={ref}
class={cn(
'flex',
emblaCtx.orientation === 'horizontal' ? '-ms-px' : '-mt-px flex-col',
className
)}
data-embla-container=""
{...restProps}
>
{@render children?.()}
</div>
</div>
Usage
<script lang="ts">
import * as Carousel from '$lib/components/ui/carousel/index.js';
</script> <Carousel.Root>
<Carousel.Content>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> Examples
Sizes
To set the size of the items, you can use the basis utility class on the <Carousel.Item />.
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
const SLIDES = [1, 2, 3, 4, 5];
</script>
<Carousel.Root
opts={{
align: "start"
}}
class="w-full max-w-sm"
>
<Carousel.Content>
{#each SLIDES as slide (slide)}
<Carousel.Item class="md:basis-1/2 lg:basis-1/3">
<div class="p-1">
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-3xl font-semibold">{slide}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> <!-- 33% of the carousel width. -->
<Carousel.Root>
<Carousel.Content>
<Carousel.Item class="basis-1/3">...</Carousel.Item>
<Carousel.Item class="basis-1/3">...</Carousel.Item>
<Carousel.Item class="basis-1/3">...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> <!-- 50% on small screens and 33% on larger screens. -->
<Carousel.Root>
<Carousel.Content>
<Carousel.Item class="md:basis-1/2 lg:basis-1/3">...</Carousel.Item>
<Carousel.Item class="md:basis-1/2 lg:basis-1/3">...</Carousel.Item>
<Carousel.Item class="md:basis-1/2 lg:basis-1/3">...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> Spacing
To set the spacing between the items, we use a ps-[VALUE] utility on the <Carousel.Item /> and a negative -ms-[VALUE] on the <Carousel.Content />.
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
const SLIDES = [1, 2, 3, 4, 5];
</script>
<Carousel.Root class="w-full max-w-sm">
<Carousel.Content class="-ms-1">
{#each SLIDES as slide (slide)}
<Carousel.Item class="ps-1 md:basis-1/2 lg:basis-1/3">
<div class="p-1">
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-2xl font-semibold">{slide}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> <Carousel.Root>
<Carousel.Content class="-ms-4">
<Carousel.Item class="ps-4">...</Carousel.Item>
<Carousel.Item class="ps-4">...</Carousel.Item>
<Carousel.Item class="ps-4">...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> <Carousel.Root>
<Carousel.Content class="-ms-2 md:-ms-4">
<Carousel.Item class="ps-2 md:ps-4">...</Carousel.Item>
<Carousel.Item class="ps-2 md:ps-4">...</Carousel.Item>
<Carousel.Item class="ps-2 md:ps-4">...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> Orientation
Use the orientation prop to set the orientation of the carousel.
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
const SLIDES = [1, 2, 3, 4, 5];
</script>
<Carousel.Root
opts={{
align: "start"
}}
orientation="vertical"
class="w-full max-w-xs"
>
<Carousel.Content class="-mt-1 h-[200px]">
{#each SLIDES as slide (slide)}
<Carousel.Item class="pt-1 md:basis-1/2">
<div class="p-1">
<Card.Root>
<Card.Content class="flex items-center justify-center p-6">
<span class="text-3xl font-semibold">{slide}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> <Carousel.Root orientation="vertical | horizontal">
<Carousel.Content>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> Options
You can pass options to the carousel using the opts prop. See the Embla Carousel docs for more information.
<Carousel.Root
opts={{
align: 'start',
loop: true
}}
>
<Carousel.Content>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> API
Use reactive state and the setApi callback to get an instance of the carousel API.
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
import type { CarouselAPI } from "$lib/components/ui/carousel/context.js";
let api = $state<CarouselAPI>();
const count = $derived(api ? api.scrollSnapList().length : 0);
let current = $state(0);
const SLIDES = [1, 2, 3, 4, 5];
$effect(() => {
if (api) {
current = api.selectedScrollSnap() + 1;
api.on("select", () => {
current = api!.selectedScrollSnap() + 1;
});
}
});
</script>
<div>
<Carousel.Root
setApi={(emblaApi) => (api = emblaApi)}
class="w-full max-w-xs"
>
<Carousel.Content>
{#each SLIDES as slide (slide)}
<Carousel.Item>
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-4xl font-semibold">{slide}</span>
</Card.Content>
</Card.Root>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root>
<div class="text-muted-foreground py-2 text-center text-sm">
Slide {current} of {count}
</div>
</div> <script lang="ts">
import { type CarouselAPI } from '$lib/components/ui/carousel/context.js';
import * as Carousel from '$lib/components/ui/carousel/index.js';
let api = $state<CarouselAPI>();
let current = $state(0);
const count = $derived(api ? api.scrollSnapList().length : 0);
$effect(() => {
if (api) {
current = api.selectedScrollSnap() + 1;
api.on('select', () => {
current = api!.selectedScrollSnap() + 1;
});
}
});
</script>
<Carousel.Root setApi={(emblaApi) => (api = emblaApi)}>
<Carousel.Content>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> Events
You can listen to events using the api instance from bind:api.
<script lang="ts">
import { type CarouselAPI } from '$lib/components/ui/carousel/context.js';
import * as Carousel from '$lib/components/ui/carousel/index.js';
let api = $state<CarouselAPI>();
$effect(() => {
if (api) {
api.on('select', () => {
// do something
});
}
});
</script>
<Carousel.Root setApi={(emblaApi) => (api = emblaApi)}>
<Carousel.Content>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
<Carousel.Item>...</Carousel.Item>
</Carousel.Content>
</Carousel.Root> Plugins
You can use the plugins prop to add plugins to the carousel.
<script lang="ts">
import Autoplay from 'embla-carousel-autoplay';
import * as Carousel from '$lib/components/ui/carousel/index.js';
</script>
<Carousel.Root
plugins={[
Autoplay({
delay: 2000
})
]}
>
<!-- ... -->
</Carousel.Root> <script lang="ts">
import Autoplay from "embla-carousel-autoplay";
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
const plugin = Autoplay({ delay: 2000, stopOnInteraction: true });
const SLIDES = [1, 2, 3, 4, 5];
</script>
<Carousel.Root
plugins={[plugin]}
class="w-full max-w-xs"
onmouseenter={plugin.stop}
onmouseleave={plugin.reset}
>
<Carousel.Content>
{#each SLIDES as slide (slide)}
<Carousel.Item>
<div class="p-1">
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-4xl font-semibold">{slide}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> See the Embla Carousel docs for more information on using plugins.