Navigation controls with the Invoker Commands API
Blossom Carousel now ships first-class navigation controls through <BlossomPrev>, <BlossomNext>, and <BlossomDots>. These components work across the Vue, React, Svelte, and Web Components packages, and they can live anywhere in your layout.
Link them to a carousel with a simple id / for pairing and Blossom handles the rest. No template ref, no layout constraints and no imperative API.
<BlossomCarousel id="gallery" class="carousel">
<div data-blossom-slide class="slide">Slide 01</div>
<div data-blossom-slide class="slide">Slide 02</div>
<div data-blossom-slide class="slide">Slide 03</div>
...
</BlossomCarousel>
<BlossomPrev for="gallery" />
<BlossomDots for="gallery" />
<BlossomNext for="gallery" />
Mark each slide with data-blossom-slide so dot navigation knows which elements to track. You choose how you want to build your slides markup—flat, nested or grouped—and you decide which elements you want to be tracked.
How they work
Under the hood, each control is a native <button type="button"> wired to the browser's Invoker Commands API:
commandforpoints at the carousel'sidcommandnames the action to perform
Blossom uses custom commands in the --blossom-* namespace:
| Control | Command |
|---|---|
| BlossomPrev | --blossom-prev |
| BlossomNext | --blossom-next |
| BlossomDots (slide n) | --blossom-goto-{n} |
When you click a control, the browser dispatches a command event on the target carousel. Blossom listens for that event and scrolls the container using native Element.scrollIntoView({ behavior: 'smooth', inline: … }), with full awareness of scroll snap alignment, scroll padding, and RTL direction.
If no snap targets are found, it falls back to a proportional scrollBy().
The controls and carousel stay decoupled and layout independent. As long as for matches the carousel's id it works.
Why Invoker Commands
Blossom is built on a simple idea: enhance native scrolling, don't replace it. Navigation controls had to follow the same rules and for Blossom to feel like a native element it has to be able to exist on its own.
Most carousel libraries expose imperative APIs — grab a ref, call .next(), wire up click handlers in JavaScript or force a template ref around the carousel and controls to create a local scope. These patterns work, but it couples your controls to your component tree and to Blossom's runtime. Invoker Commands invert that: the button declares what it wants to do, the carousel responds.
The absense of an imperative API also means controls work even when Blossom's drag engine isn't loaded. On touch devices, where Blossom skips the drag bundle entirely, Blossom's navigation controls still scroll the carousel through native behavior.
Since Invoker Commands only reached Baseline in early 2026, Blossom still includes a lightweight fallback for browsers without native commandForElement support.
What you don't have to think about
The navigation layer handles the details:
- Disabled state — prev/next buttons disable automatically at the first and last slide
- Active dot tracking — BlossomDots observes scroll position and highlights the current slide
- RTL — scroll direction is respected when
direction: rtlis set on the carousel - Scroll snap — navigation finds snap-aligned pages and scrolls to the correct inline position
You style the controls however you want using simple CSS overwrites and flexible slotted content.
<!-- using a slotted svg icon -->
<BlossomPrev for="gallery">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="m12 19-7-7 7-7" />
<path d="M19 12H5" />
</svg>
</BlossomPrev>
<!-- Thumbnail navigation using Vue's passthrough slots -->
<BlossomDots for="gallery">
<template #default="{ index, active }">
<img :class="{ active }" :src="thumbnails[i]" />
</template>
</BlossomDots>
Explore further:
- Buttons example — prev/next controls with CSS and Tailwind
- Dots example — dot navigation and custom dot UI







