XDSCarousel@xds/core · Carousel
Preview coming soon

Usage

Carousel scrolls a row of items horizontally when they overflow the available width. Use it for card grids, image galleries, product lists, or any set of items that should be browsable without taking up the full page.

Best practices

GuidancePractices
DoEnable scroll-snap when each item should land precisely at the start edge, like a gallery or product list.
DoAlways provide an aria-label that describes what the carousel contains, like "Featured products" or "Team members".
DoUse a consistent gap and item width so the carousel looks intentional, not like content overflowing by accident.
Don'tUse a carousel for content every user must see — not everyone scrolls horizontally. Put critical content above the fold.
Don'tAuto-advance items — let the user scroll at their own pace.
Don'tNest carousels — a carousel inside a carousel is confusing and breaks keyboard navigation.

Anatomy

ElementDescription
Scroll containerrequiredThe horizontal overflow area that holds all items.
ItemsrequiredThe children rendered in a row. Each item is animated with a scroll-driven scale effect.
Fade edgesrequiredGradient fades on the left and right edges that indicate more content is available.
Navigation buttonsPrev/next buttons that appear on hover. Enabled by default, disable with hasButtons={false}.

Import

ts
import {XDSCarousel} from '@xds/core/Carousel'

Props

PropTypeDescription
childrenrequired
ReactNodeCarousel items rendered in a horizontal scroll container.
gap
0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 (default: 1)Gap between items using the spacing token scale.
hasButtons
boolean (default: true)Show prev/next navigation buttons on hover (desktop only).
hasSnap
boolean (default: false)Enable scroll-snap so each child snaps to the start edge.
aria-label
string (default: 'Carousel')Accessible label for the carousel region.
ref
React.Ref<HTMLDivElement>Ref forwarded to the root element.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value.
className
stringCSS class name for the root element. Prefer xstyle for styling.
style
CSSPropertiesInline styles for the root element. Prefer xstyle.
data-testid
stringTest selector for automated testing frameworks.

Examples

Common configurations, variations, and states.
Carousel — Cards
tsx
'use client';
import {XDSCarousel} from '@xds/core/Carousel';
import {XDSCard} from '@xds/core/Card';
import {XDSStack} from '@xds/core/Layout';
import {XDSText} from '@xds/core/Text';
const FEATURES = [
{
title: 'Design System',
desc: 'Tokens, components, and patterns',
},
{
title: 'Documentation',
desc: 'API reference and usage guides',
},
{
title: 'Sandbox',
desc: 'Visual testing and previews',
},
{
title: 'Library',
desc: 'Component and hook information',
},
{
title: 'Contributing',
desc: 'Open source development',
},
];
export default function CarouselCards() {
return (
<XDSStack direction="vertical" gap={3} style={{maxWidth: 520, padding: 8}}>
<XDSText type="body" weight="bold">
Browse features
</XDSText>
<XDSCarousel gap={2} hasSnap aria-label="Feature cards">
{FEATURES.map(item => (
<XDSCard key={item.title} width={200} minHeight={100}>
<XDSStack direction="vertical" gap={1}>
<XDSText type="body" weight="bold">
{item.title}
</XDSText>
<XDSText type="supporting" color="secondary">
{item.desc}
</XDSText>
</XDSStack>
</XDSCard>
))}
</XDSCarousel>
</XDSStack>
);
}
Carousel — SnapScroll-snap carousel with navigation buttons and team member cards. Each card snaps to the start edge on scroll. Use when items should be viewed one at a time rather than as a continuous strip.
tsx
'use client';
import {XDSCarousel} from '@xds/core/Carousel';
import {XDSAvatar} from '@xds/core/Avatar';
import {XDSBadge} from '@xds/core/Badge';
import {XDSCard} from '@xds/core/Card';
import {XDSStack} from '@xds/core/Layout';
import {XDSText} from '@xds/core/Text';
const TEAM = [
{name: 'Alice Chen', role: 'Engineering Lead', color: 'blue' as const},
{name: 'Bob Smith', role: 'Product Designer', color: 'purple' as const},
{name: 'Carol Davis', role: 'Product Manager', color: 'green' as const},
{name: 'Andrew Thomas', role: 'Design Manager', color: 'red' as const},
{name: 'Gina Wilson', role: 'Software Engineer', color: 'orange' as const},
];
export default function CarouselSnap() {
return (
<XDSStack direction="vertical" gap={3} style={{maxWidth: 520, padding: 8}}>
<XDSText type="body" weight="bold">
Team members
</XDSText>
<XDSCarousel gap={2} hasSnap hasButtons aria-label="Team members">
{TEAM.map(person => (
<XDSCard key={person.name} width={180} minHeight={140}>
<XDSStack direction="vertical" gap={3} hAlign="center">
<XDSAvatar name={person.name} size="medium" />
<XDSStack direction="vertical" gap={1} hAlign="center">
<XDSText type="body" weight="bold">
{person.name}
</XDSText>
<XDSBadge variant={person.color} label={person.role} />
</XDSStack>
</XDSStack>
</XDSCard>
))}
</XDSCarousel>
</XDSStack>
);
}

Showcase source

tsx
'use client';
import {XDSCarousel} from '@xds/core/Carousel';
import {XDSCard} from '@xds/core/Card';
import {XDSStack} from '@xds/core/Layout';
import {XDSText, XDSHeading} from '@xds/core/Text';
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({
root: {
maxWidth: 500,
},
card: {
minWidth: 200,
},
});
const ITEMS = [
{title: 'Design', body: 'Create wireframes and prototypes.'},
{title: 'Develop', body: 'Build components and pages.'},
{title: 'Test', body: 'Write tests and fix bugs.'},
{title: 'Deploy', body: 'Ship to production.'},
{title: 'Monitor', body: 'Track performance and errors.'},
];
export default function CarouselShowcase() {
return (
<XDSCarousel
gap={2}
hasSnap
aria-label="Workflow steps"
xstyle={styles.root}>
{ITEMS.map(item => (
<XDSCard key={item.title} padding={3} xstyle={styles.card}>
<XDSStack direction="vertical" gap={1}>
<XDSHeading level={5}>{item.title}</XDSHeading>
<XDSText type="supporting" color="secondary">
{item.body}
</XDSText>
</XDSStack>
</XDSCard>
))}
</XDSCarousel>
);
}