HorizontalScroller
Render items in a horizontally scrolling row with an optional title, progress ring and edge fade.
HorizontalScroller is a client component ("use client") that renders items in an overflowX="scroll" flex row. Use it for card rows and tag lists that should scroll sideways instead of wrapping. It uses motion/react to track horizontal scroll progress for a circular progress indicator and an animated gradient mask, and ResizeObserver to detect overflow.
Release history
January
February
March
June
September
November
Import#
import { HorizontalScroller } from "@reactberry/system/blocks";
Usage#
import { HorizontalScroller } from "@reactberry/system/blocks";
import { Box, Text } from "@reactberry/system/elements";
const releases = [
{ id: "r1", name: "v2.0", date: "March" },
{ id: "r2", name: "v2.1", date: "June" },
{ id: "r3", name: "v2.2", date: "September" },
];
export default function ReleaseRow() {
return (
<HorizontalScroller
title="Recent releases"
items={releases}
gap="m"
renderItem={(release) => (
<Box minWidth="16rem" skin="surface" shape="rounded" p="m">
<Text fontWeight="600">{release.name}</Text>
<Text as="p" color="secondary">{release.date}</Text>
</Box>
)}
/>
);
}
Examples#
Plain row#
Turn off progressIndicator and withMask, and leave out title, for a bare scrolling row.
API#
HorizontalScroller#
| Prop | Type | Default | Description |
|---|---|---|---|
items* | any[] | — | Data to render. |
renderItem* | (item: any, index: number) => ReactNode | — | Renders each item. Each result is wrapped in a |
gap | string | number | small | Gap between items. |
progressIndicator | boolean | true | Shows a 2rem circular ring that fills with scroll progress. |
withMask | boolean | true | Fades the edges with a gradient mask while the content overflows. |
title | string | — | Rendered as an |
No other props are accepted; nothing is spread onto the root.
Accessibility#
- The progress ring has no accessible label.
- The scroll row is not keyboard-focusable by default. Ensure items contain focusable elements or add
tabIndexinsiderenderItemoutput where needed.
Notes#
- Each rendered item is wrapped in a
Boxkeyed by its index, so give items a fixed or minimum width to stop them shrinking. - The mask fades the right edge at the start, the left edge at the end, and both edges in between. It is only applied when the content is wider than the container, re-checked on resize and when
itemschanges. - The progress ring is drawn in the
brandcolour with a fixed light translucent track, which may have low contrast on light backgrounds. - The row always uses
overflow-x: scroll, so some platforms show a scrollbar even when content fits. - The outer wrapper cannot be styled through props; wrap the component in a
Boxfor spacing.