How to Use the Astryx Carousel Component for Image/Content Rotation
The Astryx Carousel component is a flexible, accessible horizontal-scroll container that automatically adds fade-edge masks, navigation buttons, and RTL support while exposing imperative methods like scrollNext() and scrollTo() for programmatic control.
The Astryx Carousel component in the facebook/astryx repository provides a production-ready solution for displaying horizontally scrollable content rows without consuming full viewport width. Built with StyleX for theme-aware styling and comprehensive accessibility features, this component handles overflow indication, navigation controls, and keyboard interactions out of the box.
Importing the Carousel Component
The Carousel is exported from the core package entry point defined in packages/core/src/Carousel/index.ts. Import the component and its TypeScript definitions directly from the package:
import {Carousel} from '@astryxdesign/core';
import type {CarouselHandle} from '@astryxdesign/core';
Basic Implementation
At its core, the Carousel wraps children in a scrollable container. In packages/core/src/Carousel/Carousel.tsx, the component renders a div with overflow-x: auto (referenced internally as styles.scroller) that enables native horizontal scrolling:
import {Carousel} from '@astryxdesign/core';
import {Thumbnail} from '@astryxdesign/core';
export function ImageCarousel() {
return (
<Carousel gap={1} aria-label="Featured photos">
<Thumbnail src="/images/a.jpg" alt="Photo A" />
<Thumbnail src="/images/b.jpg" alt="Photo B" />
<Thumbnail src="/images/c.jpg" alt="Photo C" />
</Carousel>
);
}
The gap prop applies spacing between items using StyleX tokens, while aria-label defines the accessible name for the carousel region.
Core Architecture and Scroll Mechanics
The component's architecture centers on the useScrollOverflow hook, which monitors the scrollable container's dimensions to drive UI state. This hook determines when content overflows its bounds, triggering the visibility of fade-edge masks and navigation buttons.
Overflow Indicators and Fade Masks
When content exceeds the visible area, the Carousel applies CSS-based masks to indicate scrollability. The implementation in Carousel.tsx utilizes three StyleX style objects:
styles.fadeStart: Gradient mask at the left edgestyles.fadeEnd: Gradient mask at the right edgestyles.fadeBoth: Masks applied to both edges when scrolling is possible in either direction
These masks provide visual affordances without requiring additional DOM elements.
Input Handling and Scroll Behavior
The Carousel supports multiple input methods through event handlers defined in the source:
- Native trackpad gestures leverage the browser's standard horizontal scroll behavior on the
styles.scrollerelement. - Mouse wheel support maps vertical wheel deltas to horizontal scrolling via the
handleWheelcallback, enabling horizontal navigation when users hold Shift while scrolling.
Navigation buttons trigger the scrollBy(direction) function, which respects RTL layouts and calculates scroll distance based on item dimensions and the gap prop.
Navigation Controls and Looping
By default, the Carousel renders Previous and Next buttons that appear only when scrolling is possible in the respective direction, as determined by canScrollPrev() and canScrollNext() logic within the useScrollOverflow hook.
Enable infinite looping by setting the hasLoop prop to true. When activated, the scrollBy function wraps around: clicking Next at the end scrolls to the first item, and Previous at the start jumps to the last item, keeping both buttons persistently visible.
import {Carousel} from '@astryxdesign/core';
import {Card} from '@astryxdesign/core';
export function ProductCarousel() {
return (
<Carousel
gap={2}
hasSnap
hasLoop
padding={2}
aria-label="Featured products"
>
<Card padding={4}>Product 1</Card>
<Card padding={4}>Product 2</Card>
<Card padding={4}>Product 3</Card>
<Card padding={4}>Product 4</Card>
</Carousel>
);
}
The hasSnap prop enables CSS scroll-snap for precise item alignment during scrolling.
Imperative Control with Handle Refs
For programmatic navigation, the Carousel exposes an imperative handle via the handleRef prop. This ref provides a CarouselHandle interface with five methods defined in packages/core/src/Carousel/Carousel.tsx:
scrollNext(): Advances to the next item (respectshasLoop)scrollPrev(): Returns to the previous itemscrollTo(index): Jumps to a specific zero-based indexcanScrollNext(): Returns boolean indicating if next scroll is possiblecanScrollPrev(): Returns boolean indicating if previous scroll is possible
import {useRef} from 'react';
import {Carousel, type CarouselHandle} from '@astryxdesign/core';
export function ControlledCarousel() {
const carouselRef = useRef<CarouselHandle>(null);
const goToThird = () => {
carouselRef.current?.scrollTo(2); // zero-based index
};
return (
<>
<button onClick={goToThird}>Go to slide 3</button>
<Carousel handleRef={carouselRef} hasButtons={false} aria-label="Gallery">
{/* slides */}
</Carousel>
</>
);
}
This pattern is useful for syncing external controls or deep-linking to specific slides.
Accessibility Implementation
The Carousel implements the WAI-ARIA Carousel pattern with semantic attributes set in packages/core/src/Carousel/Carousel.tsx:
- The root element carries
role="region",aria-roledescription="carousel", and anaria-label(defaulting to "Carousel") - Each child slide wrapper receives
role="group",aria-roledescription="slide", and an accessible label like "Slide 2 of 5" generated via the translator utility - Navigation buttons maintain visible focus indicators and respect reduced-motion preferences
These attributes ensure screen reader users understand the component's structure and their current position within the rotation.
Configuration Options
Control the Carousel's appearance and behavior through these props:
gap: Space between items (number, theme token multiplier)padding: Internal container padding (number, theme token multiplier)hasSnap: Boolean enabling CSS scroll-snap alignmenthasLoop: Boolean enabling infinite scroll wrappinghasButtons: Boolean toggling Prev/Next navigation buttons (default: true)hasEdgeFade: Boolean toggling overflow gradient masks (default: true)handleRef: React ref exposingCarouselHandlefor imperative control
To create a minimal carousel without auxiliary UI elements:
import {Carousel} from '@astryxdesign/core';
export function SimpleCarousel() {
return (
<Carousel hasButtons={false} hasEdgeFade={false} aria-label="Team members">
{/* slides */}
</Carousel>
);
}
Summary
- The Astryx Carousel component provides a horizontal-scroll container with automatic overflow detection and visual fade masks defined in
packages/core/src/Carousel/Carousel.tsx. - Navigation supports trackpad gestures, Shift+mouse wheel, and optional Prev/Next buttons with RTL adaptation and
hasLoopwrapping. - Imperative control is available via
handleRefexposingscrollNext(),scrollPrev(),scrollTo(index), and state-checking methods. - Accessibility includes
role="region",aria-roledescription="carousel", and slide-specific labels for screen reader compatibility. - StyleX drives all styling, making the component theme-aware and token-driven.
Frequently Asked Questions
How do I programmatically navigate to a specific slide in the Astryx Carousel?
Use the imperative handleRef API. Create a ref with useRef<CarouselHandle>(null), pass it to the handleRef prop, and call ref.current?.scrollTo(index) where index is zero-based. This method is defined in packages/core/src/Carousel/Carousel.tsx and respects the hasLoop configuration.
Does the Carousel component support right-to-left (RTL) languages?
Yes, the Carousel automatically adapts to RTL layouts. The scrollBy function and navigation button chevrons adjust their direction based on document direction. The underlying scroll behavior and fade masks (styles.fadeStart/styles.fadeEnd) swap positions to match the reading direction.
How do I disable the navigation buttons and edge fade effects?
Set the hasButtons and hasEdgeFade props to false. This removes the Prev/Next button overlay and the CSS gradient masks (styles.fadeBoth) while preserving the horizontal scroll functionality and accessibility attributes.
What accessibility features are built into the Astryx Carousel?
According to the source code in packages/core/src/Carousel/Carousel.tsx, the root element has role="region" and aria-roledescription="carousel", while each slide receives role="group" and aria-roledescription="slide" with generated labels indicating position (e.g., "Slide 2 of 5"). The component also respects reduced-motion preferences and provides keyboard-navigable controls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →