Carousel
A flexible, accessible, and customizable carousel component.
Basic Usage
Pass an array of items to carouselItems. Enable showArrow for the prev/next arrows and showDots for the position indicators.
<template>
<HLCarousel id="carousel-default" :carouselItems="items" :showArrow="true" :showDots="true" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Without Arrows or Dots
Set :showDots="false" alongside the default showArrow to hide all built-in navigation chrome and show the slides on their own. showDots defaults to true, so it has to be turned off explicitly.
With no arrows or dots, nothing on screen advances the carousel — pair it with autoPlay, mousewheel, draggable, or your own external controls, otherwise the deck sits on the first slide.
<template>
<!-- No arrows, no dots — autoPlay drives the slides -->
<HLCarousel
id="carousel-no-controls"
:carouselItems="items"
:showArrow="false"
:showDots="false"
autoPlay
:interval="1000"
/>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Loop
loop defaults to true, so the carousel wraps around endlessly and the prev/next arrows stay active on every slide. Set :loop="false" to stop at the ends instead — the prev arrow is disabled on the first slide and the next arrow is disabled on the last.
<template>
<HLCarousel id="carousel-no-loop" :carouselItems="items" :showArrow="true" :showDots="true" :loop="false" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Fade
Set effect="fade" to cross-fade between slides instead of sliding.
<template>
<HLCarousel id="carousel-fade" :carouselItems="items" :showArrow="true" effect="fade" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Slide
effect="slide" (the default) slides each slide horizontally into view.
<template>
<HLCarousel id="carousel-slide" :carouselItems="items" :showArrow="true" effect="slide" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Card
Set effect="card" to show the active slide centered, with the neighboring slides peeking in on each side.
<template>
<HLCarousel id="carousel-card" :carouselItems="items" :showArrow="true" effect="card" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Mousewheel
Add mousewheel to let users scroll through slides with the mouse wheel.
<template>
<HLCarousel id="carousel-mousewheel" :carouselItems="items" :showArrow="true" effect="slide" mousewheel />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Autoplay
Set autoPlay to advance slides automatically, and interval (ms) to control the delay between them. interval is ignored when autoPlay is off.
<template>
<HLCarousel id="carousel-autoplay" :carouselItems="items" :showDots="true" autoPlay :interval="2000" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Multiple Items Per View
Show more than one slide at a time with itemsPerView (a number or 'auto'). Use spaceBetweenItems for the gap (px) between them, and centeredItems to center the active slide.
<template>
<HLCarousel
id="carousel-multi"
:carouselItems="items"
:showArrow="true"
:itemsPerView="2"
:spaceBetweenItems="12"
:centeredItems="true"
/>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Dot Style and Placement
Use dotType ('dot' or 'line') to change the indicator style and dotPlacement ('top', 'bottom', 'left', 'right') to position it.
<template>
<HLCarousel id="carousel-dots" :carouselItems="items" :showDots="true" dotType="line" dotPlacement="top" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Fade Overlay
Set fadeIn and/or fadeOut to show a gradient fade on the leading/trailing edge, hinting that more slides are available. Both are off unless explicitly enabled.
<template>
<HLCarousel
id="carousel-fade-overlay"
:carouselItems="items"
:showArrow="true"
:itemsPerView="2"
:fadeIn="true"
:fadeOut="true"
/>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Custom Transition
Set effect="custom" and pass a Vue transition name via transitionProps. Define the matching transition classes in your styles.
<template>
<HLCarousel id="carousel-custom" :carouselItems="items" :showArrow="true" effect="custom" :transitionProps="{ name: 'creative' }" />
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>
<style>
.creative-enter-from,
.creative-leave-to {
opacity: 0;
transform: scale(0.8);
}
.creative-enter-active,
.creative-leave-active {
transition: all 0.3s ease;
}
</style>Controlled Usage
Bind currentIndex and listen to @update:index to drive the active slide from your own state.
<template>
<HLCarousel
id="carousel-controlled"
:carouselItems="items"
:showArrow="true"
:currentIndex="currentIndex"
show-dots
@update:index="onUpdatedIndex"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
const currentIndex = ref(1)
const onUpdatedIndex = (index: number) => {
currentIndex.value = index
}
</script>Different Size, Color and Direction
Combine size, color, direction, dot-placement, arrow-placement, and dot-type to restyle and reorient the carousel.
<template>
<HLCarousel
id="carousel-restyled"
:carouselItems="items"
:showArrow="true"
size="sm"
color="white"
show-dots
direction="vertical"
dot-placement="right"
arrow-placement="right"
dot-type="line"
/>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>Custom Render
Use the #customRender slot to provide your own slide markup — for example, wrapping each image in a link.
<template>
<HLCarousel id="carousel-custom-render" :showArrow="true">
<template #customRender>
<a href="https://google.com" target="_blank">
<img style="width: 100%; height: 240px; object-fit: cover;" src="https://picsum.photos/id/101/600/240" />
</a>
<a href="https://google.com" target="_blank">
<img style="width: 100%; height: 240px; object-fit: cover;" src="https://picsum.photos/id/238/600/240" />
</a>
<a href="https://google.com" target="_blank">
<img style="width: 100%; height: 240px; object-fit: cover;" src="https://picsum.photos/id/236/600/240" />
</a>
</template>
</HLCarousel>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
</script>Custom Arrows
Use the #customArrows and #customDots slots to render your own navigation controls. Each slot exposes the current index plus prev, next, and changeTo helpers.
<template>
<HLCarousel id="carousel-custom-arrows" :carouselItems="items" :showDots="true" :showArrow="true">
<template #customArrows="{ prev, next }">
<div class="hr-custom-arrow">
<button type="button" class="custom-arrow--left" @click="prev">
<ArrowLeftIcon class="w-5 h-5" />
</button>
<button type="button" class="custom-arrow--right" @click="next">
<ArrowRightIcon class="w-5 h-5" />
</button>
</div>
</template>
<template #customDots="{ total, currentIndex, changeTo }">
<ul class="hr-custom-dots">
<li
v-for="index of total"
:key="index"
:class="{ 'is-active': currentIndex === index - 1 }"
@click="changeTo(index - 1)"
/>
</ul>
</template>
</HLCarousel>
</template>
<script setup lang="ts">
import { HLCarousel } from '@platform-ui/highrise'
import { ArrowLeftIcon, ArrowRightIcon } from '@gohighlevel/ghl-icons/24/outline'
const items = [
{ key: '1', src: 'https://picsum.photos/id/101/300/100' },
{ key: '2', src: 'https://picsum.photos/id/238/300/100' },
{ key: '3', src: 'https://picsum.photos/id/236/300/100' },
]
</script>
<style scoped>
.hr-custom-arrow {
display: flex;
position: absolute;
bottom: 25px;
inset-inline-end: 10px;
}
.hr-custom-arrow button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
margin-inline-end: 12px;
color: var(--base-white);
background-color: rgba(255, 255, 255, 0.1);
border-width: 0;
border-radius: 8px;
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.hr-custom-arrow button:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.hr-custom-arrow button:active {
transform: scale(0.95);
transform-origin: center;
}
.hr-custom-dots {
display: flex;
margin: 0;
padding: 0;
position: absolute;
bottom: 20px;
inset-inline-start: 20px;
}
.hr-custom-dots li {
display: inline-block;
width: 12px;
height: 4px;
margin: 0 3px;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.4);
transition:
width 0.3s,
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.hr-custom-dots li.is-active {
width: 40px;
background: var(--base-white);
}
</style>Import
import { HLCarousel } from '@platform-ui/highrise'Accessibility
- Describe the slide deck via
aria-labeloraria-roledescription="carousel"onHLCarouselso users know what is being rotated. - Link navigation controls to the slide container with
aria-controls/aria-labelledby, and flag the active indicator viaaria-current="true". - If slides advance automatically, expose a pause/resume control that toggles
aria-pressedand set the carousel region toaria-live="polite".
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | Auto (hr-carousel-*) | Unique identifier for the carousel. Generated automatically when omitted. |
| showArrow | boolean | false | Show navigation arrows |
| autoPlay | boolean | false | Enable automatic slide transition |
| direction | 'horizontal' | 'vertical' | 'horizontal' | Carousel direction |
| dotPlacement | 'left' | 'right' | 'bottom' | 'top' | 'bottom' | Position of navigation dots |
| arrowPlacement | 'top' | 'bottom' | 'left' | 'right' | 'center' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'bottomRight' | Position of navigation arrows |
| dotType | 'dot' | 'line' | 'dot' | Style of navigation dots |
| itemsPerView | number | 'auto' | 1 | Number of items visible per view |
| spaceBetweenItems | number | 0 | Space (px) between items |
| draggable | boolean | false | Allow dragging to navigate |
| loop | boolean | true | Wrap around endlessly. When false, the carousel stops at the ends and the prev/next arrows are disabled on the first/last slide |
| centeredItems | boolean | false | Center items in the carousel |
| mousewheel | boolean | false | Enable mouse wheel navigation |
| showDots | boolean | true | Show navigation dots |
| interval | number | 500 | Autoplay interval (ms) this will be ignored if autoPlay is false |
| effect | 'card' | 'slide' | 'fade' | 'custom' | 'slide' | Transition effect |
| size | 'sm' | 'md' | 'lg' | 'md' | Size of the carousel |
| color | 'blue' | 'gray' | 'white' | 'blue' | Color of the carousel |
| touchable | boolean | true | Enable touch navigation |
| carouselItems | HLCarouselItem[] | [] | Array of items to display (see below). Each item can define alt text for accessibility. |
| classNames | string | - | Custom class names for the root element |
| currentIndex | number | - | Controlled current index (for external control) |
| transitionProps | TransitionProps | - | Transition props for the carousel |
| transitionStyle | { transitionDuration?: string; transitionTimingFunction?: string } | { transitionDuration: '300ms' } | Transition style for the carousel |
| fadeIn | boolean | undefined | Show a gradient fade overlay on the leading edge once past the first slide. Off unless explicitly set to true. |
| fadeOut | boolean | undefined | Show a gradient fade overlay on the trailing edge while more slides remain. Off unless explicitly set to true. |
HLCarouselItem Type
interface HLCarouselItem {
key: string
src: string
alt?: string
class?: string
}Emits
| Name | Parameters | Description |
|---|---|---|
@update:index | (currentIndex: number, lastIndex: number) | Fired when the active slide changes; receives the new index and the previous one |
Slots
| Name | Slot props | Description |
|---|---|---|
| customRender | () | Replace the auto-generated slides from carouselItems with your own markup. When used, carouselItems is not required |
| customArrows | { total, currentIndex, to, prev, next } | Render custom prev/next controls (shown only when showArrow is set). Call prev() / next() to step, or to(index) to jump |
| customDots | { total, currentIndex, changeTo } | Render custom position indicators (shown only when showDots is set). Call changeTo(index) to jump to a slide |