Skip to main content

Carousel

Overview

Use the Carousel family when you need swipeable or keyboard-navigable slides with optional autoplay, looping, synced instances, and external navigation buttons. The root component owns the Swiper.js runtime configuration, while Carousel Slide defines each slide. External Carousel Previous Button, Carousel Next Button, and Carousel Dots components can be placed anywhere on the page and target a specific carousel by ID. Multiple carousels sharing a sync group ID stay aligned automatically — ideal for main-content-plus-thumbnail patterns.

Authoring Structure

Carousel
├── Carousel Slide
│ └── slide content
├── Carousel Slide
│ └── slide content
└── Carousel Slide
└── slide content

Carousel Previous Button (anywhere on page)
Carousel Next Button (anywhere on page)
Carousel Dots (anywhere on page)

Placement Rules

ComponentPlacementRole
CarouselTop-level wrapper.Owns the Swiper runtime, slide configuration, IDs, and sync settings.
Carousel SlideDirect child of Carousel.Each direct child becomes one navigable slide.
Carousel Previous ButtonAnywhere on the page.Targets a carousel by ID and moves it to the previous slide.
Carousel Next ButtonAnywhere on the page.Targets a carousel by ID and moves it to the next slide.
Carousel DotsAnywhere on the page.Renders clickable pagination bullets for a targeted carousel.

Quick Start

Basic — slides only
<OmeCarousel settings='{{"slidesPerView":"1","spaceBetween":"16"}}'>
{#slot default}
<OmeCarouselSlide>
{#slot default}<div>Slide 1</div>{/slot}
</OmeCarouselSlide>
<OmeCarouselSlide>
{#slot default}<div>Slide 2</div>{/slot}
</OmeCarouselSlide>
{/slot}
</OmeCarousel>
With external navigation and dots
<OmeCarousel
identity='{{"id":"featured-gallery"}}'
settings='{{"slidesPerView":"1","spaceBetween":"16"}}'
>
{#slot default}
<OmeCarouselSlide>
{#slot default}<img src="/slide-1.jpg" alt="Slide 1" />{/slot}
</OmeCarouselSlide>
<OmeCarouselSlide>
{#slot default}<img src="/slide-2.jpg" alt="Slide 2" />{/slot}
</OmeCarouselSlide>
{/slot}
</OmeCarousel>

<OmeCarouselPrevButton targeting='{{"for":"featured-gallery"}}' />
<OmeCarouselDots targeting='{{"for":"featured-gallery"}}' />
<OmeCarouselNextButton targeting='{{"for":"featured-gallery"}}' />
Synced carousels (main + thumbnails)
<OmeCarousel
identity='{{"id":"main-carousel","groupId":"product-sync"}}'
settings='{{"slidesPerView":"1","loop":"true"}}'
>
{#slot default}
<OmeCarouselSlide>
{#slot default}<img src="/product-1.jpg" alt="" />{/slot}
</OmeCarouselSlide>
<OmeCarouselSlide>
{#slot default}<img src="/product-2.jpg" alt="" />{/slot}
</OmeCarouselSlide>
{/slot}
</OmeCarousel>

<OmeCarousel
identity='{{"id":"thumb-carousel","groupId":"product-sync"}}'
settings='{{"slidesPerView":"4","spaceBetween":"8","loop":"true"}}'
>
{#slot default}
<OmeCarouselSlide>
{#slot default}<img src="/product-1-thumb.jpg" alt="" />{/slot}
</OmeCarouselSlide>
<OmeCarouselSlide>
{#slot default}<img src="/product-2-thumb.jpg" alt="" />{/slot}
</OmeCarouselSlide>
{/slot}
</OmeCarousel>

Family Components

The root container wraps all slides and manages the Swiper.js runtime: slide layout, navigation wiring, keyboard support, autoplay, effects, and sync groups. It renders a div (configurable via tag) with the swiper class and a child swiper-wrapper. The slot accepts Carousel Slide children directly.

Structure Props

PropTypeDefaultDescription
tagstringdiv

HTML tag for the carousel root element.

Identity Props

PropTypeDefaultDescription
idstring""

Stable carousel ID used by external buttons, dots, and synced carousels to target this instance. Required when using any external control.

groupIdstring""

Shared sync group ID. Carousels with the same group ID stay synchronized — sliding one advances the others.

Settings Props

PropTypeDefaultDescription
initialSlidestring"0"

Zero-based slide index that is active on first load.

loopbooleanfalse

Wraps from the last slide back to the first instead of disabling navigation at the ends.

centeredSlidesbooleanfalse

Centers the active slide in the viewport.

slidesPerViewstring"1"

Number of slides visible at once. Use "auto" to size slides from CSS widths.

spaceBetweenstring"0"

Gap between slides in pixels.

keyboardEnabledbooleantrue

Enables left and right arrow key navigation when the carousel root is focused.

enableResponsivebooleanfalse

Enables breakpoint-based slides per view and spacing overrides.

Responsive Controls Props

PropTypeDefaultDescription
breakpointstring

Minimum viewport width in pixels where this row starts applying.

slidesPerViewstring

Slides per view at this breakpoint. Use "auto" to size slides from CSS widths.

spaceBetweenstring

Gap between slides in pixels at this breakpoint.

Autoplay Props

PropTypeDefaultDescription
autoplaybooleanfalse

Advances slides automatically.

autoplayDelaystring"3000"

Delay between autoplay transitions in milliseconds.

disableOnInteractionbooleanfalse

Stops autoplay after user interactions (swipe, click).

pauseOnMouseEnterbooleanfalse

Pauses autoplay while the pointer is over the carousel.

stopOnLastSlidebooleanfalse

Stops autoplay when the last slide is reached in non-loop mode.

reverseDirectionbooleanfalse

Runs autoplay toward previous slides instead of next slides.

Effects Props

PropTypeDefaultDescription
effect"slide" | "fade" | "coverflow" | "flip" | "cube" | "cards" | "creative"slide

Active Swiper transition effect. "slide" is the lightweight default — other effects load their runtime only when selected.

crossFadebooleantrue

Fade effect. Fades slides over each other instead of briefly showing content underneath.

rotatestring"50"

Coverflow effect. Slide rotation in degrees.

depthstring"100"

Coverflow effect. Depth offset in pixels.

stretchstring"0"

Coverflow effect. Spacing stretch between slides in pixels or percent.

modifierstring"1"

Coverflow effect. Effect multiplier.

scalestring"1"

Coverflow effect. Slide scale effect.

slideShadowsbooleantrue

Coverflow / Flip / Cube / Cards effects. Adds shadows to slides.

limitRotationbooleantrue

Flip effect. Limits edge slide rotation.

shadowbooleantrue

Cube effect. Adds a main cube shadow.

shadowOffsetstring"20"

Cube effect. Main shadow offset in pixels.

shadowScalestring"0.94"

Cube effect. Main shadow scale ratio.

perSlideOffsetstring"8"

Cards effect. Offset distance per slide in pixels.

perSlideRotatestring"2"

Cards effect. Rotation angle per slide in degrees.

rotatebooleantrue

Cards effect. Enables cards rotation.

creativeEffectobject{}

Creative effect. Swiper creativeEffect object for advanced transforms.

Custom Parameters Props

PropTypeDefaultDescription
useCustomConfigbooleanfalse

Uses the JSON config object as the source of truth for Swiper behavior while keeping identity-driven controls (navigation, dots, sync) wired automatically.

jsonConfigobject{}

Custom Swiper configuration object. Effect selection and identity wiring stay controlled by Etch — the JSON refines behavior like breakpoints, autoplay, and keyboard options.

Styling Props

PropTypeDefaultDescription
classclassome-carousel

CSS class applied to the carousel root element. The swiper class is always included.

Settings and Autoplay Visibility

The Settings and Autoplay prop groups are visible only when useCustomConfig is false. When custom config mode is on, those props are hidden and the jsonConfig object becomes the behavior source of truth instead.

Sync Groups

Carousels that share the same identity.groupId stay synchronized via Swiper's Controller module. This is useful for main-carousel-plus-thumbnail patterns — sliding one carousel automatically advances the other. At least two carousels must share a group ID for sync to activate.

In non-loop mode, external navigation buttons receive a data-ome-carousel-nav-disabled attribute at the boundaries:

  • The Previous button is disabled at the first slide.
  • The Next button is disabled at the last slide.

When loop is true, buttons never disable — the carousel wraps around instead.

Keyboard Behavior

When the carousel root has focus:

KeyAction
ArrowRightAdvance to the next slide.
ArrowLeftGo back to the previous slide.

Keyboard navigation is enabled by default (keyboardEnabled: true) and scoped to the viewport (onlyInViewport: true). Disable it by setting keyboardEnabled to false.

Accessibility

The root component wires up ARIA attributes via Swiper's A11y module:

  • The carousel container announces "Content carousel" as its role message.
  • Navigation buttons have aria-label defaults ("Previous slide", "Next slide"), overridable via each button's label prop.
  • Pagination dots announce "Go to slide N" via aria-label.
  • The Swiper A11y module provides live-region announcements for slide changes, including "This is the first slide" and "This is the last slide" messages.

Each CarouselSlide represents a single slide inside the carousel. It renders as a <div> with the swiper-slide class and accepts any content in its default slot.

Styling Props

PropTypeDefaultDescription
classclass

CSS class names applied to the slide container.


An external <button> element that navigates a targeted carousel backward. It can be placed anywhere on the page — it does not need to be inside the carousel root. When the slot is empty, a default left-arrow SVG icon is rendered automatically.

Targeting Props

PropTypeDefaultDescription
forstring""

Target carousel ID. This must match identity.id on the carousel root you want to control.

Accessibility Props

PropTypeDefaultDescription
labelstring"Previous slide"

Accessible label announced by assistive technology.

Styling Props

PropTypeDefaultDescription
classclassome-carousel-prev-button-default

CSS class names applied to the button element.

Fallback Icon

When the button slot is empty, a default left-arrow SVG icon is rendered. Provide slot content only when you want custom button UI (text, icon, or both).

Boundary Behavior

At the first slide, the previous button receives data-ome-carousel-nav-disabled when looping is off. With settings.loop="true", the carousel wraps instead of disabling the button.


An external <button> element that navigates a targeted carousel forward. Identical behavior to the Previous Button but in the opposite direction. When the slot is empty, a default right-arrow SVG icon is rendered automatically.

Targeting Props

PropTypeDefaultDescription
forstring""

Target carousel ID. This must match identity.id on the carousel root you want to control.

Accessibility Props

PropTypeDefaultDescription
labelstring"Next slide"

Accessible label announced by assistive technology.

Styling Props

PropTypeDefaultDescription
classclassome-carousel-next-button-default

CSS class names applied to the button element.

Fallback Icon

When the button slot is empty, a default right-arrow SVG icon is rendered. Provide slot content only when you want custom button UI.

Boundary Behavior

At the last slide, the next button receives data-ome-carousel-nav-disabled when looping is off. With settings.loop="true", the carousel wraps instead of disabling the button.


External pagination dots that render one clickable bullet per slide. The dots container can be placed anywhere on the page and targets a carousel by ID. At runtime, the template dot markup is extracted from the component and used to generate the correct number of bullets dynamically.

Targeting Props

PropTypeDefaultDescription
forstring""

Target carousel ID. This must match identity.id on the carousel root.

Styling Props

PropTypeDefaultDescription
classclassome-carousel-dots-default

CSS class names applied to the dots wrapper element.

dotClassclassome-carousel-dot-default

CSS class names applied to each generated dot button. The active dot also receives ome-carousel-dot-active and swiper-pagination-bullet-active.

Dot Generation

On initialization, the dots component extracts a hidden template button (data-ome-carousel-dot-template) and uses it to generate the correct number of bullets via Swiper's renderBullet callback. Each bullet is a <button> with aria-label="Go to slide N". The active bullet receives the ome-carousel-dot-active class in addition to Swiper's default active class.


CSS Custom Properties

/* Disabled state */
[data-ome-carousel-prev][data-ome-carousel-nav-disabled],
[data-ome-carousel-next][data-ome-carousel-nav-disabled] {
cursor: not-allowed;
opacity: 0.5;
}

Navigation buttons receive data-ome-carousel-nav-disabled when the carousel is at the boundary in non-loop mode. Use this attribute to style the disabled state.

Pagination Dots

.ome-carousel-dots-default {
display: flex;
align-items: center;
gap: 0.5rem;
}

.ome-carousel-dot-default {
width: 0.75rem;
height: 0.75rem;
padding: 0;
border: 0;
border-radius: 999px;
background: currentColor;
opacity: 0.35;
cursor: pointer;
}

.ome-carousel-dot-default.ome-carousel-dot-active,
.ome-carousel-dot-default.swiper-pagination-bullet-active {
opacity: 1;
}
[data-ome-carousel-root] {
width: 100%;
position: relative;
overflow: hidden;
}

[data-ome-carousel-root][data-ome-carousel-initialized] {
touch-action: pan-y;
}

The carousel root always receives data-ome-carousel-root. After initialization, data-ome-carousel-initialized is added and touch-action: pan-y is applied for swipe support.


Common Mistakes

Forgetting to set a Carousel ID for external controls

External buttons and dots target a carousel by ID. If identity.id is empty on the root, previous buttons, next buttons, and dots cannot connect. The Etch editor shows a warning callout when the ID is missing.

Placing content directly inside Carousel without Carousel Slide

Only Carousel Slide children become navigable slides. Putting raw HTML or other blocks directly inside the carousel root (without wrapping them in a slide) breaks the Swiper layout.

Expecting navigation buttons to stay enabled at boundaries without loop

In non-loop mode, the previous button disables at the first slide and the next button disables at the last slide. Enable settings.loop="true" if you need wrap-around navigation.

Trying to change the effect via custom JSON config

Custom JSON config can refine the selected effect's parameters but cannot activate a different effect. The effects.effect prop controls which effect loads. For example, setting effect: "fade" in the JSON has no effect when effects.effect is "slide".


FAQs

How do I sync two carousels together?

Set the same identity.groupId value on both carousel roots. The runtime uses Swiper's Controller module to keep them aligned. Each carousel also needs its own unique identity.id. This is commonly used for a main content carousel paired with a thumbnail strip.

Can I have multiple sets of navigation buttons for the same carousel?

Yes. You can place multiple Carousel Previous Button and Carousel Next Button components on the page, all targeting the same carousel ID. The runtime updates all matching buttons when the slide changes or the carousel reaches a boundary.

How do I make slides responsive at different viewport widths?

Set enableResponsive to true and add breakpoint rows. Each row specifies a minimum viewport width (breakpoint), slidesPerView, and spaceBetween. For example, a breakpoint at 768 with slidesPerView: 2 shows two slides on screens 768px and wider.

When should I use custom config mode?

Use useCustomConfig: true when you need Swiper behavior that the built-in settings and autoplay props don't cover — for example, custom freeMode, slidesPerGroup, or advanced breakpoints configurations. The custom JSON becomes the source of truth for behavior, but identity-driven wiring (navigation buttons, dots, sync groups) stays connected automatically.

Do navigation buttons and dots need to be inside the carousel?

No. Carousel Previous Button, Carousel Next Button, and Carousel Dots can be placed anywhere on the page. They connect to the carousel by matching their targeting.for prop to the carousel's identity.id.