Carousel
This feature is available with Motion+
The Carousel component creates performant, accessible and fully-featured carousels in React. It's designed to be flexible and easy to use, supporting pointer, wheel and keyboard navigation out the box.
It allows you to go beyond the traditional limitations of CSS-only approaches, with support for infinitely-scrolling carousels and without limitations on styling.
Features
Accessible: Automatic ARIA labels, respects reduced motion, RTL layouts, and all major input methods.
Performant: Built on the same unique rendering used by the Ticker component that achieves infinite scrolling with while minimising or eliminating item cloning.
Customisable: Provides functions and state to easily create custom controls and pagination.
It has a simple API that lets you pass an array of items:
<Carousel items={items} />
The carousel can be swiped via pointer, or scrolled with a wheel. Custom controls can be built with the useCarousel hook, which provides action functions, as well as some state information like totalPages, currentPage etc.
This ensures you can build controls and pagination indicators with your own component libraries.
<Carousel loop={false} items={items}> <Next /> </Carousel>
function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Native web carousels built using scroll have the hard technical limit of not being able to loop or extend outside their layout. With Carousel, you can place the carousel within a body of content and have the items visually extend out to the edges of the viewport by enabling overflow, as well as infinitely repeating.
Install
First, add the Motion+ library to your project using your private token. You need to be a Motion+ member to generate a private token.
npm install https://api.motion.dev/registry\?package\=motion-plus\&version\=2.0.0\&token
Once installed, Carousel can be imported via motion-plus/react:
import { Carousel } from "motion-plus/react"
Usage
Import
Import the Carousel component from "motion-plus/react"`:
import { Carousel } from "motion-plus/react"
Carousel accepts a single mandatory prop, items. This is a list of valid React nodes (which can be components, strings or numbers):
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
Direction
By default, carousels will scroll horizontally. Setting the axis prop to y, we can make them vertical.
<Carousel items={items} axis="y" />
Layout
Items are laid out via flexbox. Passing gap and align will adjust the spacing and off-axis alignment of them items.
<Carousel items={items} gap={0} align="start" />
Overflow
By setting overflow to true, items will visually extend out from the container to the edges of the viewport.
<Carousel items={items} overflow />
This makes it straightforward to place a Carousel within a document flow but still extend the ticker effect across the full viewport.
Infinite scrolling
By default, carousels will scroll infinitely. This can be disabled by setting loop={false}.
<Carousel items={items} loop={false} />
Layout
By default, each item will be sized according to its contents. By setting itemSize="fill", items will extend the match the width of the container.
<Carousel items={items} itemSize="fill" />
Snapping
By default, drag and wheel controls will snap between pages. By setting snap={false}, snapping can be disabled and the carousel will freely scroll.
<Carousel items={items} snap={false} />
Custom controls
Custom controls can be passed to Carousel as children.
<Carousel loop={false} items={items}> <Next /> </Carousel>
Any component rendered within Carousel can call useCarousel to access state and pagination functions. This hook provides:
nextPage/prevPage: Paginate next/previous.gotoPage: Pass it a page index to animate to this page.isNextActive/isPrevActive: Ifloop={false}then these will be false when we hit the limits of the carousel.currentPage: Index of the current pagetotalPages: Number of total pages.
import { useCarousel } from "motion-plus/react" function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Autoplay
With currentPage and nextPage from useCarousel, we can also set up our own autoplay functionality.
By passing currentPage to the useEffect, the timer will restart whenever the page changes, whether that's from a swipe/drag, or from the autoplay timer itself.
const { currentPage, nextPage } = useCarousel() const progress = useMotionValue(0) useEffect(() => { const animation = animate(progress, [0, 1], { duration, ease: "linear", onComplete: nextPage, }) return () => animation.stop() }, [duration, nextPage, progress, currentPage])
Pagination visualisation
By using currentPage, totalPages and gotoPage from useCarousel, a custom pagination indicator/navigator can be built.
function Pagination() { const { currentPage, totalPages, gotoPage } = useCarousel() return ( <ul className="dots"> {Array.from({ length: totalPages }, (_, index) => ( <li className="dot"> <motion.button initial={false} animate={{ opacity: currentPage === index ? 1 : 0.5 }} onClick={() => gotoPage(index)} /> </li> )} </ul> ) }
Animate items with carousel scroll
Carousel renders a Ticker under-the-hood, so it has full access to the useTickerItem hook. useTickerItem returns information about the item that you can use to create bespoke per-item animations.
It returns:
offset: AMotionValuethat represents the item's scroll offset relative to the container. At0, the item is visually aligned to the start of the ticker. Note that this will be flipped in RTL layouts.start/end: The start and end boundaries of the item layout within the ticker. Note that these are reversed in RTL layouts.itemIndex: Theindexof this item. For clones, thisindexvalue will represent the index value of the original item.
function Item() { const { offset } = useTickerItem() const rotate = useTransform(offset, [0, 300], [0, 360], { clamp: false }) return ( <motion.div style={{ rotate }}> 😂 </motion.div> ) }
<Carousel items={[<Item />]} />
Options
Carousel renders a <motion /> component, so it accepts most of the same props. It also supports the following options:
items
Required. An array of strings or React components to render. If looping, this list will be cloned as many times as needed to visually fill the ticker.
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
axis
Default: "x"
Determines on which axis the ticker should lay out and scroll.
<Carousel items={items} axis="y" />
gap
Default: 10
A gap to apply between items, in pixels.
align
Default: "center"
Alignment of items within the ticker's off-axis. For example, if this is a y-axis ticker, this will align items horizontally within the ticker.
Can be set to "start", "center" or "end".
loop
Default: true
Whether to infinitely loop items. If set to false, items won't be cloned and pagination will stop at either end of the carousel.
<Carousel items={items} loop={false} />
overflow
Default: false
Whether to overflow items beyond the constraints of the carousel.
<Carousel items={items} overflow />
snap
Default: "page"
By default, drag and wheel scroll will snap to the nearest page. This can be disabled by setting snap={false}.
<Carousel items={items} snap={false} />
fade
Default: 0
Fades the content out at each end of the carousel container. Can be set either as a number (pixels) or %.
When loop={false}, the fade will automatically disappear when the edges of the carousel are reached.
<Carousel items={items} fade={100} />
safeMargin
Default: 0
Ticker uses a unique reprojection renderer, which can reduce or eliminate item cloning. Technically, this works by reprojecting items that disappear off the start of the visible area over to the end.
The calculation for this is based on the item and ticker container's layout. If you're rotating the ticker, or transforming its items in some way that brings them back inside the visible area, you may see items disappear as they reproject. If so, you can use safeMargin to increase the visible area to compensate.
<Carousel items={items} safeMargin={100} />
as
Default: "div"
The HTML element to use to render the carousel container.
<Carousel items={items} overflow />
The Carousel component creates performant, accessible and fully-featured carousels in React. It's designed to be flexible and easy to use, supporting pointer, wheel and keyboard navigation out the box.
It allows you to go beyond the traditional limitations of CSS-only approaches, with support for infinitely-scrolling carousels and without limitations on styling.
Features
Accessible: Automatic ARIA labels, respects reduced motion, RTL layouts, and all major input methods.
Performant: Built on the same unique rendering used by the Ticker component that achieves infinite scrolling with while minimising or eliminating item cloning.
Customisable: Provides functions and state to easily create custom controls and pagination.
It has a simple API that lets you pass an array of items:
<Carousel items={items} />
The carousel can be swiped via pointer, or scrolled with a wheel. Custom controls can be built with the useCarousel hook, which provides action functions, as well as some state information like totalPages, currentPage etc.
This ensures you can build controls and pagination indicators with your own component libraries.
<Carousel loop={false} items={items}> <Next /> </Carousel>
function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Native web carousels built using scroll have the hard technical limit of not being able to loop or extend outside their layout. With Carousel, you can place the carousel within a body of content and have the items visually extend out to the edges of the viewport by enabling overflow, as well as infinitely repeating.
Install
First, add the Motion+ library to your project using your private token. You need to be a Motion+ member to generate a private token.
npm install https://api.motion.dev/registry\?package\=motion-plus\&version\=2.0.0\&token
Once installed, Carousel can be imported via motion-plus/react:
import { Carousel } from "motion-plus/react"
Usage
Import
Import the Carousel component from "motion-plus/react"`:
import { Carousel } from "motion-plus/react"
Carousel accepts a single mandatory prop, items. This is a list of valid React nodes (which can be components, strings or numbers):
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
Direction
By default, carousels will scroll horizontally. Setting the axis prop to y, we can make them vertical.
<Carousel items={items} axis="y" />
Layout
Items are laid out via flexbox. Passing gap and align will adjust the spacing and off-axis alignment of them items.
<Carousel items={items} gap={0} align="start" />
Overflow
By setting overflow to true, items will visually extend out from the container to the edges of the viewport.
<Carousel items={items} overflow />
This makes it straightforward to place a Carousel within a document flow but still extend the ticker effect across the full viewport.
Infinite scrolling
By default, carousels will scroll infinitely. This can be disabled by setting loop={false}.
<Carousel items={items} loop={false} />
Layout
By default, each item will be sized according to its contents. By setting itemSize="fill", items will extend the match the width of the container.
<Carousel items={items} itemSize="fill" />
Snapping
By default, drag and wheel controls will snap between pages. By setting snap={false}, snapping can be disabled and the carousel will freely scroll.
<Carousel items={items} snap={false} />
Custom controls
Custom controls can be passed to Carousel as children.
<Carousel loop={false} items={items}> <Next /> </Carousel>
Any component rendered within Carousel can call useCarousel to access state and pagination functions. This hook provides:
nextPage/prevPage: Paginate next/previous.gotoPage: Pass it a page index to animate to this page.isNextActive/isPrevActive: Ifloop={false}then these will be false when we hit the limits of the carousel.currentPage: Index of the current pagetotalPages: Number of total pages.
import { useCarousel } from "motion-plus/react" function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Autoplay
With currentPage and nextPage from useCarousel, we can also set up our own autoplay functionality.
By passing currentPage to the useEffect, the timer will restart whenever the page changes, whether that's from a swipe/drag, or from the autoplay timer itself.
const { currentPage, nextPage } = useCarousel() const progress = useMotionValue(0) useEffect(() => { const animation = animate(progress, [0, 1], { duration, ease: "linear", onComplete: nextPage, }) return () => animation.stop() }, [duration, nextPage, progress, currentPage])
Pagination visualisation
By using currentPage, totalPages and gotoPage from useCarousel, a custom pagination indicator/navigator can be built.
function Pagination() { const { currentPage, totalPages, gotoPage } = useCarousel() return ( <ul className="dots"> {Array.from({ length: totalPages }, (_, index) => ( <li className="dot"> <motion.button initial={false} animate={{ opacity: currentPage === index ? 1 : 0.5 }} onClick={() => gotoPage(index)} /> </li> )} </ul> ) }
Animate items with carousel scroll
Carousel renders a Ticker under-the-hood, so it has full access to the useTickerItem hook. useTickerItem returns information about the item that you can use to create bespoke per-item animations.
It returns:
offset: AMotionValuethat represents the item's scroll offset relative to the container. At0, the item is visually aligned to the start of the ticker. Note that this will be flipped in RTL layouts.start/end: The start and end boundaries of the item layout within the ticker. Note that these are reversed in RTL layouts.itemIndex: Theindexof this item. For clones, thisindexvalue will represent the index value of the original item.
function Item() { const { offset } = useTickerItem() const rotate = useTransform(offset, [0, 300], [0, 360], { clamp: false }) return ( <motion.div style={{ rotate }}> 😂 </motion.div> ) }
<Carousel items={[<Item />]} />
Options
Carousel renders a <motion /> component, so it accepts most of the same props. It also supports the following options:
items
Required. An array of strings or React components to render. If looping, this list will be cloned as many times as needed to visually fill the ticker.
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
axis
Default: "x"
Determines on which axis the ticker should lay out and scroll.
<Carousel items={items} axis="y" />
gap
Default: 10
A gap to apply between items, in pixels.
align
Default: "center"
Alignment of items within the ticker's off-axis. For example, if this is a y-axis ticker, this will align items horizontally within the ticker.
Can be set to "start", "center" or "end".
loop
Default: true
Whether to infinitely loop items. If set to false, items won't be cloned and pagination will stop at either end of the carousel.
<Carousel items={items} loop={false} />
overflow
Default: false
Whether to overflow items beyond the constraints of the carousel.
<Carousel items={items} overflow />
snap
Default: "page"
By default, drag and wheel scroll will snap to the nearest page. This can be disabled by setting snap={false}.
<Carousel items={items} snap={false} />
fade
Default: 0
Fades the content out at each end of the carousel container. Can be set either as a number (pixels) or %.
When loop={false}, the fade will automatically disappear when the edges of the carousel are reached.
<Carousel items={items} fade={100} />
safeMargin
Default: 0
Ticker uses a unique reprojection renderer, which can reduce or eliminate item cloning. Technically, this works by reprojecting items that disappear off the start of the visible area over to the end.
The calculation for this is based on the item and ticker container's layout. If you're rotating the ticker, or transforming its items in some way that brings them back inside the visible area, you may see items disappear as they reproject. If so, you can use safeMargin to increase the visible area to compensate.
<Carousel items={items} safeMargin={100} />
as
Default: "div"
The HTML element to use to render the carousel container.
<Carousel items={items} overflow />
The Carousel component creates performant, accessible and fully-featured carousels in React. It's designed to be flexible and easy to use, supporting pointer, wheel and keyboard navigation out the box.
It allows you to go beyond the traditional limitations of CSS-only approaches, with support for infinitely-scrolling carousels and without limitations on styling.
Features
Accessible: Automatic ARIA labels, respects reduced motion, RTL layouts, and all major input methods.
Performant: Built on the same unique rendering used by the Ticker component that achieves infinite scrolling with while minimising or eliminating item cloning.
Customisable: Provides functions and state to easily create custom controls and pagination.
It has a simple API that lets you pass an array of items:
<Carousel items={items} />
The carousel can be swiped via pointer, or scrolled with a wheel. Custom controls can be built with the useCarousel hook, which provides action functions, as well as some state information like totalPages, currentPage etc.
This ensures you can build controls and pagination indicators with your own component libraries.
<Carousel loop={false} items={items}> <Next /> </Carousel>
function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Native web carousels built using scroll have the hard technical limit of not being able to loop or extend outside their layout. With Carousel, you can place the carousel within a body of content and have the items visually extend out to the edges of the viewport by enabling overflow, as well as infinitely repeating.
Install
First, add the Motion+ library to your project using your private token. You need to be a Motion+ member to generate a private token.
npm install https://api.motion.dev/registry\?package\=motion-plus\&version\=2.0.0\&token
Once installed, Carousel can be imported via motion-plus/react:
import { Carousel } from "motion-plus/react"
Usage
Import
Import the Carousel component from "motion-plus/react"`:
import { Carousel } from "motion-plus/react"
Carousel accepts a single mandatory prop, items. This is a list of valid React nodes (which can be components, strings or numbers):
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
Direction
By default, carousels will scroll horizontally. Setting the axis prop to y, we can make them vertical.
<Carousel items={items} axis="y" />
Layout
Items are laid out via flexbox. Passing gap and align will adjust the spacing and off-axis alignment of them items.
<Carousel items={items} gap={0} align="start" />
Overflow
By setting overflow to true, items will visually extend out from the container to the edges of the viewport.
<Carousel items={items} overflow />
This makes it straightforward to place a Carousel within a document flow but still extend the ticker effect across the full viewport.
Infinite scrolling
By default, carousels will scroll infinitely. This can be disabled by setting loop={false}.
<Carousel items={items} loop={false} />
Layout
By default, each item will be sized according to its contents. By setting itemSize="fill", items will extend the match the width of the container.
<Carousel items={items} itemSize="fill" />
Snapping
By default, drag and wheel controls will snap between pages. By setting snap={false}, snapping can be disabled and the carousel will freely scroll.
<Carousel items={items} snap={false} />
Custom controls
Custom controls can be passed to Carousel as children.
<Carousel loop={false} items={items}> <Next /> </Carousel>
Any component rendered within Carousel can call useCarousel to access state and pagination functions. This hook provides:
nextPage/prevPage: Paginate next/previous.gotoPage: Pass it a page index to animate to this page.isNextActive/isPrevActive: Ifloop={false}then these will be false when we hit the limits of the carousel.currentPage: Index of the current pagetotalPages: Number of total pages.
import { useCarousel } from "motion-plus/react" function Next() { const { nextPage, isNextActive } = useCarousel() return ( <button disabled={!isNextActive} onClick={nextPage}> Next </button> ) }
Autoplay
With currentPage and nextPage from useCarousel, we can also set up our own autoplay functionality.
By passing currentPage to the useEffect, the timer will restart whenever the page changes, whether that's from a swipe/drag, or from the autoplay timer itself.
const { currentPage, nextPage } = useCarousel() const progress = useMotionValue(0) useEffect(() => { const animation = animate(progress, [0, 1], { duration, ease: "linear", onComplete: nextPage, }) return () => animation.stop() }, [duration, nextPage, progress, currentPage])
Pagination visualisation
By using currentPage, totalPages and gotoPage from useCarousel, a custom pagination indicator/navigator can be built.
function Pagination() { const { currentPage, totalPages, gotoPage } = useCarousel() return ( <ul className="dots"> {Array.from({ length: totalPages }, (_, index) => ( <li className="dot"> <motion.button initial={false} animate={{ opacity: currentPage === index ? 1 : 0.5 }} onClick={() => gotoPage(index)} /> </li> )} </ul> ) }
Animate items with carousel scroll
Carousel renders a Ticker under-the-hood, so it has full access to the useTickerItem hook. useTickerItem returns information about the item that you can use to create bespoke per-item animations.
It returns:
offset: AMotionValuethat represents the item's scroll offset relative to the container. At0, the item is visually aligned to the start of the ticker. Note that this will be flipped in RTL layouts.start/end: The start and end boundaries of the item layout within the ticker. Note that these are reversed in RTL layouts.itemIndex: Theindexof this item. For clones, thisindexvalue will represent the index value of the original item.
function Item() { const { offset } = useTickerItem() const rotate = useTransform(offset, [0, 300], [0, 360], { clamp: false }) return ( <motion.div style={{ rotate }}> 😂 </motion.div> ) }
<Carousel items={[<Item />]} />
Options
Carousel renders a <motion /> component, so it accepts most of the same props. It also supports the following options:
items
Required. An array of strings or React components to render. If looping, this list will be cloned as many times as needed to visually fill the ticker.
const items = [ <span>One</span>, <span>Two</span>, <span>Three</span> ] return <Carousel items={items} />
axis
Default: "x"
Determines on which axis the ticker should lay out and scroll.
<Carousel items={items} axis="y" />
gap
Default: 10
A gap to apply between items, in pixels.
align
Default: "center"
Alignment of items within the ticker's off-axis. For example, if this is a y-axis ticker, this will align items horizontally within the ticker.
Can be set to "start", "center" or "end".
loop
Default: true
Whether to infinitely loop items. If set to false, items won't be cloned and pagination will stop at either end of the carousel.
<Carousel items={items} loop={false} />
overflow
Default: false
Whether to overflow items beyond the constraints of the carousel.
<Carousel items={items} overflow />
snap
Default: "page"
By default, drag and wheel scroll will snap to the nearest page. This can be disabled by setting snap={false}.
<Carousel items={items} snap={false} />
fade
Default: 0
Fades the content out at each end of the carousel container. Can be set either as a number (pixels) or %.
When loop={false}, the fade will automatically disappear when the edges of the carousel are reached.
<Carousel items={items} fade={100} />
safeMargin
Default: 0
Ticker uses a unique reprojection renderer, which can reduce or eliminate item cloning. Technically, this works by reprojecting items that disappear off the start of the visible area over to the end.
The calculation for this is based on the item and ticker container's layout. If you're rotating the ticker, or transforming its items in some way that brings them back inside the visible area, you may see items disappear as they reproject. If so, you can use safeMargin to increase the visible area to compensate.
<Carousel items={items} safeMargin={100} />
as
Default: "div"
The HTML element to use to render the carousel container.
<Carousel items={items} overflow />

