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 autoplay off for a tour - the default 0 keeps the carousel still so readers move at their own pace, and the built-in arrows still work.

Accessibility

See also