'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselBasic = () => {
return (
<Carousel.Root slideCount={items.length} maxW="md" mx="auto">
<Carousel.ItemGroup>
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control justifyContent="center" gap="4">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronLeft />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.Indicators />
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronRight />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
</Carousel.Root>
)
}
Anatomy
import { Carousel } from '@chakra-ui/react'<Carousel.Root>
<Carousel.ItemGroup>
<Carousel.Item />
</Carousel.ItemGroup>
<Carousel.Control>
<Carousel.AutoplayTrigger>
<Carousel.AutoplayIndicator />
</Carousel.AutoplayTrigger>
<Carousel.PrevTrigger />
<Carousel.Indicators />
<Carousel.NextTrigger />
<Carousel.ProgressText />
</Carousel.Control>
</Carousel.Root>Examples
Controlled
Use the page and onPageChange props to programmatically control the active
carousel page.
'use client'
import { useState } from 'react'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselControlled = () => {
const [page, setPage] = useState(0)
return (
<Carousel.Root
slideCount={items.length}
maxW="md"
mx="auto"
page={page}
onPageChange={(e) => setPage(e.page)}
>
<Carousel.ItemGroup>
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control justifyContent="center" gap="4">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronLeft />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.Indicators />
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronRight />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
</Carousel.Root>
)
}
Arrows
Use the Carousel.PrevTrigger and Carousel.NextTrigger components to create
arrows that navigate between slides.
'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuArrowLeft, LuArrowRight } from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithFloatingArrow = () => {
return (
<Carousel.Root slideCount={items.length} maxW="xl" mx="auto" gap="4">
<Carousel.Control justifyContent="center" gap="4" width="full">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="outline">
<LuArrowLeft />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.ItemGroup width="full">
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="outline">
<LuArrowRight />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
<Carousel.Indicators />
</Carousel.Root>
)
}
Indicators
Use the Carousel.Indicators component to render visual indicators that help
users track the progress of the carousel and jump to specific slides.
It's a shortcut that renders a full set of indicators based on the number of slides, so you don't have to map over them yourself.
<Carousel.IndicatorGroup>
{Array.from({ length: items.length }, (_, index) => (
<Carousel.Indicator key={index} index={index} />
))}
</Carousel.IndicatorGroup>Use Carousel.IndicatorGroup and Carousel.Indicator directly when you need to
customize each indicator.
'use client'
import { Carousel } from '@chakra-ui/react'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithIndicators = () => {
return (
<Carousel.Root slideCount={items.length} maxW="md" mx="auto" gap="4">
<Carousel.ItemGroup>
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control justifyContent="center" gap="4">
<Carousel.Indicators />
</Carousel.Control>
</Carousel.Root>
)
}
Spacing
Use the spacing prop to control the spacing between slides.
'use client'
import { Carousel, HStack, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselSpacing = () => {
return (
<Carousel.Root
spacing="48px"
slidesPerPage={1.5}
slideCount={items.length}
maxW="xl"
mx="auto"
>
<HStack textStyle="sm" mb="4">
{"spacing='48px'"}
</HStack>
<Carousel.ItemGroup>
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control justifyContent="center" gap="4">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronLeft />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.Indicators />
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronRight />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
</Carousel.Root>
)
}
Vertical
Set the orientation prop to vertical to transform your carousel into a
vertical slider.
'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronDown, LuChevronUp } from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselVertical = () => {
return (
<Carousel.Root
orientation="vertical"
slideCount={items.length}
mx="auto"
height="320px"
maxW="xl"
>
<Carousel.ItemGroup flex="1">
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox fontSize="2.5rem">{index + 1}</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control h="100%" justifyContent="space-between" gap="4">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronUp />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.Indicators />
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronDown />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
</Carousel.Root>
)
}
Autoplay
Pass the autoplay prop to the Carousel.Root component to make the carousel
automatically move between slides.
'use client'
import { Carousel, HStack, IconButton } from '@chakra-ui/react'
import {
LuChevronLeft,
LuChevronRight,
LuClock,
LuPause,
LuPlay,
} from 'react-icons/lu'
import { DecorativeBox } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithAutoplay = () => {
return (
<Carousel.Root
autoplay={{ delay: 2000 }}
slideCount={items.length}
mx="auto"
maxW="xl"
>
<HStack textStyle="sm" mb="4">
<LuClock /> {'autoplay={{ delay: 2000 }}'} or {'autoplay={true}'}
</HStack>
<Carousel.ItemGroup>
{items.map((_, index) => (
<Carousel.Item key={index} index={index}>
<DecorativeBox w="100%" h="300px" rounded="lg" fontSize="2.5rem">
{index + 1}
</DecorativeBox>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.Control justifyContent="center" gap="4">
<Carousel.PrevTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronLeft />
</IconButton>
</Carousel.PrevTrigger>
<Carousel.AutoplayTrigger asChild>
<IconButton aria-label="Toggle autoplay" size="sm" variant="ghost">
<Carousel.AutoplayIndicator
paused={<LuPause />}
play={<LuPlay />}
/>
</IconButton>
</Carousel.AutoplayTrigger>
<Carousel.NextTrigger asChild>
<IconButton size="xs" variant="ghost">
<LuChevronRight />
</IconButton>
</Carousel.NextTrigger>
</Carousel.Control>
</Carousel.Root>
)
}
Images
Here's an example that shows how to create an image carousel for a product showcase.
'use client'
import { forwardRef } from 'react'
import type { IconButtonProps } from '@chakra-ui/react'
import { AspectRatio, Box, Carousel, IconButton, Image } from '@chakra-ui/react'
import { LuArrowLeft, LuArrowRight } from 'react-icons/lu'
export const CarouselWithImages = () => {
return (
<Carousel.Root
slideCount={items.length}
maxW="2xl"
mx="auto"
gap="4"
position="relative"
colorPalette="white"
>
<Carousel.Control gap="4" width="full" position="relative">
<Carousel.PrevTrigger asChild>
<ActionButton insetStart="4">
<LuArrowLeft />
</ActionButton>
</Carousel.PrevTrigger>
<Carousel.ItemGroup width="full">
{items.map((src, index) => (
<Carousel.Item key={index} index={index}>
<AspectRatio ratio={16 / 9} maxH="72vh" w="full">
<Image
src={src}
alt={`Product ${index + 1}`}
objectFit="contain"
/>
</AspectRatio>
</Carousel.Item>
))}
</Carousel.ItemGroup>
<Carousel.NextTrigger asChild>
<ActionButton insetEnd="4">
<LuArrowRight />
</ActionButton>
</Carousel.NextTrigger>
<Box position="absolute" bottom="6" width="full">
<Carousel.Indicators
transition="width 0.2s ease-in-out"
transformOrigin="center"
opacity="0.5"
boxSize="2"
_current={{ width: '10', bg: 'colorPalette.subtle', opacity: 1 }}
/>
</Box>
</Carousel.Control>
</Carousel.Root>
)
}
const ActionButton = forwardRef<HTMLButtonElement, IconButtonProps>(
function ActionButton(props, ref) {
return (
<IconButton
{...props}
ref={ref}
size="xs"
variant="outline"
rounded="full"
position="absolute"
zIndex="1"
bg="bg"
/>
)
},
)
const items = [
'https://images.unsplash.com/photo-1656433031375-5042f5afe894?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2371',
'https://images.unsplash.com/photo-1587466412525-87497b34fc88?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2673',
'https://images.unsplash.com/photo-1629581688635-5d88654e5bdd?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2831',
'https://images.unsplash.com/photo-1661030420948-862787de0056?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2370',
'https://images.unsplash.com/photo-1703505841379-2f863b201212?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2371',
'https://images.unsplash.com/photo-1607776905497-b4f788205f6a?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2370',
]
Props
Root
| Prop | Default | Type |
|---|---|---|
slideCount * | numberThe total number of slides. Useful for SSR to render the initial ating the snap points. | |
allowMouseDrag | false | booleanWhether to allow scrolling via dragging with mouse |
autoplay | false | boolean | { delay: number }Whether to scroll automatically. The default delay is 4000ms. |
autoSize | false | booleanWhether to enable variable width slides. |
defaultPage | '0' | numberThe initial page to scroll to when rendered. Use when you don't need to control the page of the carousel. |
inViewThreshold | '0.6' | number | number[]The threshold for determining if an item is in view. |
loop | false | booleanWhether the carousel should loop around. |
orientation | '\'horizontal\'' | 'horizontal' | 'vertical'The orientation of the element. |
slidesPerMove | '\'auto\'' | number | 'auto'The number of slides to scroll at a time. When set to `auto`, the number of slides to scroll is determined by the `slidesPerPage` property. |
slidesPerPage | '1' | numberThe number of slides to show at a time. |
snapType | '\'mandatory\'' | 'proximity' | 'mandatory'The snap type of the item. |
spacing | '\'0px\'' | stringThe amount of space between items. |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
ids | Partial<{
root: string
item: (index: number) => string
itemGroup: string
nextTrigger: string
prevTrigger: string
indicatorGroup: string
indicator: (index: number) => string
}>The ids of the elements in the carousel. Useful for composition. | |
onAutoplayStatusChange | (details: AutoplayStatusDetails) => voidFunction called when the autoplay status changes. | |
onDragStatusChange | (details: DragStatusDetails) => voidFunction called when the drag status changes. | |
onPageChange | (details: PageChangeDetails) => voidFunction called when the page changes. | |
padding | stringDefines the extra space added around the scrollable area, enabling nearby items to remain partially in view. | |
page | numberThe controlled page of the carousel. | |
translations | IntlTranslationsThe localized messages to use. |
ItemGroup
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Item
| Prop | Default | Type |
|---|---|---|
index * | numberThe index of the item. | |
snapAlign | '\'start\'' | 'center' | 'start' | 'end'The snap alignment of the item. |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Control
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |