{"slug":"carousel","title":"Carousel","description":"Using the carousel machine in your project.","contentType":"component","framework":"react","content":"an accessible carousel component that leverages native CSS Scroll Snap for\nsmooth, performant scrolling between slides.\n\n## Resources\n\n\n[Latest version: v1.31.0](https://www.npmjs.com/package/@zag-js/carousel)\n[Logic Visualizer](https://zag-visualizer.vercel.app/carousel)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/carousel)\n\n\n\n**Features**\n\n- Uses native CSS Scroll Snap\n- Supports horizontal and vertical orientations\n- Supports alignment of slides (start, center or end alignment)\n- Show multiple slides at a time\n- Supports looping and auto-playing\n- Supports custom spacing between slides\n\n## Installation\n\nTo use the carousel machine in your project, run the following command in your\ncommand line:\n\n```bash\nnpm install @zag-js/carousel @zag-js/react\n# or\nyarn add @zag-js/carousel @zag-js/react\n```\n\n## Anatomy\n\nTo set up the carousel correctly, you'll need to understand its anatomy and how\nwe name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nFirst, import the carousel package into your project\n\n```jsx\nimport * as carousel from \"@zag-js/carousel\"\n```\n\nThe carousel package exports two key functions:\n\n- `machine` — The state machine logic for the carousel widget.\n- `connect` — The function that translates the machine's state to JSX attributes\n  and event handlers.\n\n> You'll also need to provide a unique `id` to the `useMachine` hook. This is\n> used to ensure that every part has a unique identifier.\n\nNext, import the required hooks and functions for your framework and use the\ncarousel machine in your project 🔥\n\n> **Note:** The carousel requires that you provide a `slideCount` property in\n> the machine's context. This is the number of slides in the carousel.\n\n```jsx\nimport * as carousel from \"@zag-js/carousel\"\nimport { normalizeProps, useMachine } from \"@zag-js/react\"\n\nconst items = [\n  \"https://tinyurl.com/5b6ka8jd\",\n  \"https://tinyurl.com/7rmccdn5\",\n  \"https://tinyurl.com/59jxz9uu\",\n]\n\nexport function Carousel() {\n  const service = useMachine(carousel.machine, {\n    id: \"1\",\n    slideCount: items.length,\n  })\n\n  const api = carousel.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <div {...api.getControlProps()}>\n        <button {...api.getPrevTriggerProps()}>Prev</button>\n        <button {...api.getNextTriggerProps()}>Next</button>\n      </div>\n      <div {...api.getItemGroupProps()}>\n        {items.map((image, index) => (\n          <div {...api.getItemProps({ index })} key={index}>\n            <img\n              src={image}\n              alt=\"\"\n              style={{ height: \"300px\", width: \"100%\", objectFit: \"cover\" }}\n            />\n          </div>\n        ))}\n      </div>\n      <div {...api.getIndicatorGroupProps()}>\n        {api.pageSnapPoints.map((_, index) => (\n          <button {...api.getIndicatorProps({ index })} key={index} />\n        ))}\n      </div>\n    </div>\n  )\n}\n```\n\n### Vertical carousel\n\nTo create a vertical carousel, set the `orientation` property in the machine's\ncontext to `vertical`.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  orientation: \"vertical\",\n})\n```\n\n### Setting the initial slide\n\nTo set the initial slide of the carousel, pass the `defaultPage` property to the\nmachine's context.\n\n> The `defaultPage` corresponds to the scroll snap position index based on the\n> layout. It does not necessarily correspond to the index of the slide in the\n> carousel.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  defaultPage: 2,\n})\n```\n\n### Setting the number of slides to show at a time\n\nTo customize number of slides to show at a time, set the `slidesPerPage`\nproperty in the machine's context. The value must be an integer.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  slidesPerPage: 2,\n})\n```\n\n### Setting the number of slides to move at a time\n\nTo customize number of slides to move at a time, set the `slidesPerMove`\nproperty in the machine's context. The value must be an integer or `auto`.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  slidesPerMove: 2,\n})\n```\n\n**Considerations**\n\n- If the value is `auto`, the carousel will move the number of slides equal to\n  the number of slides per page.\n- Ensure the `slidesPerMove` is less than or equal to the `slidesPerPage` to\n  avoid skipping slides.\n- On touch devices, `slidesPerMove` is not enforced during active swiping. The\n  browser's native scrolling and CSS Scroll Snap determine slide movement for\n  optimal performance and UX.\n\n### Setting the carousel should loop around\n\nTo allow looping of slides, set the `loop` property in the machine's context to\n`true`.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  loop: true,\n})\n```\n\n### Setting the gap between slides\n\nTo customize spacing between slides, set the `spacing` property in the machine's\ncontext to a valid CSS unit.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  spacing: \"16px\",\n})\n```\n\n### Listening for page changes\n\nWhen the carousel page changes, the `onPageChange` callback is invoked.\n\n```jsx {2-5}\nconst service = useMachine(carousel.machine, {\n  onPageChange(details) {\n    // details => { page: number }\n    console.log(\"selected page:\", details.page)\n  },\n})\n```\n\n### Dragging the carousel\n\nTo allow dragging the carousel with the mouse, set the `allowMouseDrag` property\nin the machine's context to `true`.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  allowMouseDrag: true,\n})\n```\n\n### Autoplaying the carousel\n\nTo allow the carousel to autoplay, set the `autoplay` property in the machine's\ncontext to `true`.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  autoplay: true,\n})\n```\n\nAlternatively, you can configure the autoplay interval by setting the `delay`\nproperty in the machine's context.\n\n```jsx {2}\nconst service = useMachine(carousel.machine, {\n  autoplay: { delay: 2000 },\n})\n```\n\n## Styling guide\n\nEarlier, we mentioned that each carousel part has a `data-part` attribute added\nto them to select and style them in the DOM.\n\n```css\n[data-part=\"root\"] {\n  /* styles for the root part */\n}\n\n[data-part=\"item-group\"] {\n  /* styles for the item-group part */\n}\n\n[data-part=\"item\"] {\n  /* styles for the root part */\n}\n\n[data-part=\"control\"] {\n  /* styles for the control part */\n}\n\n[data-part=\"next-trigger\"] {\n  /* styles for the next-trigger part */\n}\n\n[data-part=\"prev-trigger\"] {\n  /* styles for the prev-trigger part */\n}\n\n[data-part=\"indicator-group\"] {\n  /* styles for the indicator-group part */\n}\n\n[data-part=\"indicator\"] {\n  /* styles for the indicator part */\n}\n\n[data-part=\"autoplay-trigger\"] {\n  /* styles for the autoplay-trigger part */\n}\n```\n\n### Active state\n\nWhen a carousel's indicator is active, a `data-current` attribute is set on the\nindicator.\n\n```css\n[data-part=\"indicator\"][data-current] {\n  /* styles for the indicator's active state */\n}\n```\n\n## Methods and Properties\n\nThe carousel's `api` exposes the following methods and properties:\n\n### Machine Context\n\nThe carousel machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; item: (index: number) => string; itemGroup: string; nextTrigger: string; prevTrigger: string; indicatorGroup: string; indicator: (index: number) => string; }>`\nDescription: The ids of the elements in the carousel. Useful for composition.\n\n**`translations`**\nType: `IntlTranslations`\nDescription: The localized messages to use.\n\n**`slidesPerPage`**\nType: `number`\nDescription: The number of slides to show at a time.\n\n**`autoSize`**\nType: `boolean`\nDescription: Whether to enable variable width slides.\n\n**`slidesPerMove`**\nType: `number | \"auto\"`\nDescription: The number of slides to scroll at a time.\n\nWhen set to `auto`, the number of slides to scroll is determined by the\n`slidesPerPage` property.\n\n**`autoplay`**\nType: `boolean | { delay: number; }`\nDescription: Whether to scroll automatically. The default delay is 4000ms.\n\n**`allowMouseDrag`**\nType: `boolean`\nDescription: Whether to allow scrolling via dragging with mouse\n\n**`loop`**\nType: `boolean`\nDescription: Whether the carousel should loop around.\n\n**`page`**\nType: `number`\nDescription: The controlled page of the carousel.\n\n**`defaultPage`**\nType: `number`\nDescription: The initial page to scroll to when rendered.\nUse when you don't need to control the page of the carousel.\n\n**`spacing`**\nType: `string`\nDescription: The amount of space between items.\n\n**`padding`**\nType: `string`\nDescription: Defines the extra space added around the scrollable area,\nenabling nearby items to remain partially in view.\n\n**`onPageChange`**\nType: `(details: PageChangeDetails) => void`\nDescription: Function called when the page changes.\n\n**`inViewThreshold`**\nType: `number | number[]`\nDescription: The threshold for determining if an item is in view.\n\n**`snapType`**\nType: `\"proximity\" | \"mandatory\"`\nDescription: The snap type of the item.\n\n**`slideCount`**\nType: `number`\nDescription: The total number of slides.\nUseful for SSR to render the initial ating the snap points.\n\n**`onDragStatusChange`**\nType: `(details: DragStatusDetails) => void`\nDescription: Function called when the drag status changes.\n\n**`onAutoplayStatusChange`**\nType: `(details: AutoplayStatusDetails) => void`\nDescription: Function called when the autoplay status changes.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n**`orientation`**\nType: `\"horizontal\" | \"vertical\"`\nDescription: The orientation of the element.\n\n### Machine API\n\nThe carousel `api` exposes the following methods:\n\n**`page`**\nType: `number`\nDescription: The current index of the carousel\n\n**`pageSnapPoints`**\nType: `number[]`\nDescription: The current snap points of the carousel\n\n**`isPlaying`**\nType: `boolean`\nDescription: Whether the carousel is auto playing\n\n**`isDragging`**\nType: `boolean`\nDescription: Whether the carousel is being dragged. This only works when `draggable` is true.\n\n**`canScrollNext`**\nType: `boolean`\nDescription: Whether the carousel is can scroll to the next view\n\n**`canScrollPrev`**\nType: `boolean`\nDescription: Whether the carousel is can scroll to the previous view\n\n**`scrollToIndex`**\nType: `(index: number, instant?: boolean) => void`\nDescription: Function to scroll to a specific item index\n\n**`scrollTo`**\nType: `(page: number, instant?: boolean) => void`\nDescription: Function to scroll to a specific page\n\n**`scrollNext`**\nType: `(instant?: boolean) => void`\nDescription: Function to scroll to the next page\n\n**`scrollPrev`**\nType: `(instant?: boolean) => void`\nDescription: Function to scroll to the previous page\n\n**`getProgress`**\nType: `() => number`\nDescription: Returns the current scroll progress as a percentage\n\n**`getProgressText`**\nType: `() => string`\nDescription: Returns the progress text\n\n**`play`**\nType: `VoidFunction`\nDescription: Function to start/resume autoplay\n\n**`pause`**\nType: `VoidFunction`\nDescription: Function to pause autoplay\n\n**`isInView`**\nType: `(index: number) => boolean`\nDescription: Whether the item is in view\n\n**`refresh`**\nType: `VoidFunction`\nDescription: Function to re-compute the snap points\nand clamp the page\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: carousel\n**`data-part`**: root\n**`data-orientation`**: The orientation of the carousel\n\n**`ItemGroup`**\n\n**`data-scope`**: carousel\n**`data-part`**: item-group\n**`data-orientation`**: The orientation of the item\n**`data-dragging`**: Present when in the dragging state\n\n**`Item`**\n\n**`data-scope`**: carousel\n**`data-part`**: item\n**`data-index`**: The index of the item\n**`data-inview`**: Present when in viewport\n**`data-orientation`**: The orientation of the item\n\n**`Control`**\n\n**`data-scope`**: carousel\n**`data-part`**: control\n**`data-orientation`**: The orientation of the control\n\n**`PrevTrigger`**\n\n**`data-scope`**: carousel\n**`data-part`**: prev-trigger\n**`data-orientation`**: The orientation of the prevtrigger\n\n**`NextTrigger`**\n\n**`data-scope`**: carousel\n**`data-part`**: next-trigger\n**`data-orientation`**: The orientation of the nexttrigger\n\n**`IndicatorGroup`**\n\n**`data-scope`**: carousel\n**`data-part`**: indicator-group\n**`data-orientation`**: The orientation of the indicatorgroup\n\n**`Indicator`**\n\n**`data-scope`**: carousel\n**`data-part`**: indicator\n**`data-orientation`**: The orientation of the indicator\n**`data-index`**: The index of the item\n**`data-readonly`**: Present when read-only\n**`data-current`**: Present when current\n\n**`AutoplayTrigger`**\n\n**`data-scope`**: carousel\n**`data-part`**: autoplay-trigger\n**`data-orientation`**: The orientation of the autoplaytrigger\n**`data-pressed`**: Present when pressed\n\n### CSS Variables\n\n<CssVarTable name=\"carousel\" />","package":"@zag-js/carousel","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/carousel.mdx"}