SvCarousel
A slideshow: a sliding track with prev/next arrows, dot indicators, optional autoplay that pauses on hover, and touch swipe.
SvCarousel renders your slides through a slide snippet - it hands you the
index and you return the content - so it works equally well for images, cards,
or onboarding steps. The active slide is bindable, autoplay pauses on hover and
focus, and dragging past a threshold advances the track. Colors and radius come
from the grid's --sg-* tokens.
Basic usage
<script lang="ts">
import { SvCarousel } from '@svgrid/grid'
const imgs = ['/a.jpg', '/b.jpg', '/c.jpg']
</script>
<SvCarousel count={imgs.length} autoplay={4000}>
{#snippet slide(i)}<img src={imgs[i]} alt="" />{/snippet}
</SvCarousel>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
count |
number |
- | Number of slides. The slide snippet is called per index. |
slide |
Snippet<[number]> |
- | Renders one slide; receives the slide index. |
current |
number |
0 |
Active slide index. Bindable with bind:current. |
autoplay |
number |
0 |
Autoplay interval in ms; 0 is off. Pauses on hover/focus. |
loop |
boolean |
true |
Wraps past the ends instead of stopping. |
arrows |
boolean |
true |
Shows prev/next arrows (hidden when there is one slide). |
dots |
boolean |
true |
Shows the dot indicator row. |
ariaLabel |
string |
'Carousel' |
Accessible name for the carousel region. |
Patterns
Bind the active slide
Use bind:current to drive or read the position from outside - handy for a
custom counter or synced controls:
<script lang="ts">
let current = $state(0)
</script>
<SvCarousel {count} bind:current>
{#snippet slide(i)}<Card data={items[i]} />{/snippet}
</SvCarousel>
<p>Slide {current + 1} of {count}</p>
Autoplay that pauses on hover
Set an interval; the carousel stops advancing while the pointer or focus is inside, and resumes on leave:
<SvCarousel count={banners.length} autoplay={5000} loop>
{#snippet slide(i)}<Banner {...banners[i]} />{/snippet}
</SvCarousel>
Non-looping with arrows only
Turn off loop and dots for a stepped gallery where the arrows disable at the
ends:
<SvCarousel count={steps.length} loop={false} dots={false}>
{#snippet slide(i)}<Step {...steps[i]} />{/snippet}
</SvCarousel>
An onboarding tour
Drive a stepped welcome flow with bind:current and a non-looping track, then
show a "Get started" button once the reader reaches the last slide:
<script lang="ts">
import { SvCarousel, SvButton } from '@svgrid/grid'
const steps = [
{ title: 'Welcome', body: 'Here is your new workspace.' },
{ title: 'Build a screen', body: 'Drag fields onto the canvas.' },
{ title: 'Ship it', body: 'Publish and share a link.' },
]
let current = $state(0)
const onLast = $derived(current === steps.length - 1)
</script>
<SvCarousel count={steps.length} bind:current loop={false} ariaLabel="Onboarding">
{#snippet slide(i)}
<div style="padding:32px; text-align:center;">
<h3 style="margin:0 0 6px;">{steps[i].title}</h3>
<p style="margin:0; color:var(--sg-muted);">{steps[i].body}</p>
</div>
{/snippet}
</SvCarousel>
<div style="display:flex; justify-content:flex-end; margin-top:12px;">
{#if onLast}
<SvButton variant="primary" onclick={finish}>Get started</SvButton>
{:else}
<SvButton variant="ghost" onclick={() => (current += 1)}>Next</SvButton>
{/if}
</div>
Tip: leave
autoplayoff for a tour - the default0keeps the carousel still so readers move at their own pace, and the built-in arrows still work.
Accessibility
- The region carries
role="group",aria-roledescription="carousel", and yourariaLabel; each slide is labeled "N of total" and inactive slides arearia-hidden. - Arrows and dots are real
<button>s with cleararia-labels; the active dot setsaria-current. - Autoplay pauses on hover and focus so keyboard and pointer users are not
fighting a moving target. Consider omitting
autoplayfor motion-sensitive contexts.
See also
- Feedback overview - the whole status and display layer at a glance.
- SvSkeleton - placeholders for slides that are still loading.
- SvAvatarGroup - another compact display primitive from the kit.