A flexible, responsive horizontal scroll component for React with built-in navigation controls and container queries.
- 🎯 Snap scrolling with configurable alignment
- 📱 Responsive padding, gaps, and scroll behavior via container queries
- ⌨️ Built-in keyboard navigation
- 🎨 Customizable navigation buttons
- 🔧 Flexible API with sensible defaults
- 🪶 Lightweight with zero dependencies (除了 React)
npm install @yourusername/scrollyimport{Scrolly}from"@yourusername/scrolly";functionApp(){return(<Scrollygap="1rem"paddingX="2rem"><div>Item 1</div><div>Item 2</div><div>Item 3</div></Scrolly>);}| Prop | Type | Default | Description |
|---|---|---|---|
paddingX | ResponsiveValue<string> | "0" | Horizontal padding |
paddingY | ResponsiveValue<string> | "0" | Vertical padding |
gap | ResponsiveValue<string> | "0px" | Gap between items |
| Prop | Type | Default | Description |
|---|---|---|---|
scrollSnapType | "x mandatory" | "x proximity" | "none" | "none" | Snap scrolling behavior |
scrollSnapAlign | "start" | "end" | "center" | "none" | "none" | Child snap alignment |
scrollPaddingInline | ResponsiveValue<string> | "1rem" | Padding for snap positions |
scrollMarginInline | ResponsiveValue<string> | "0" | Margin for child snap positions |
overscrollBehaviourInline | "auto" | "contain" | "none" | "contain" | Overscroll behavior |
overflowType | "scroll" | "auto" | "auto" | Overflow behavior |
| Prop | Type | Default | Description |
|---|---|---|---|
showControls | boolean | true | Show navigation buttons |
hideScrollBar | boolean | true | Hide scrollbar |
ButtonLeft | React.ComponentType<{onClick: () => void}> | - | Custom left button |
ButtonRight | React.ComponentType<{onClick: () => void}> | - | Custom right button |
containerStyles | React.CSSProperties | {} | Additional container styles |
Use responsive values for breakpoint-based styling:
<ScrollypaddingX={{0: "1rem",768: "2rem",1024: "4rem",}}gap={{0: "0.5rem",640: "1rem",1024: "2rem",}}>{/* children */}</Scrolly><ScrollyscrollSnapType="x mandatory"scrollSnapAlign="center"gap="1rem"paddingX="2rem">{images.map((img)=>(<imgkey={img.id}src={img.src}alt={img.alt}/>))}</Scrolly>import{ChevronLeft,ChevronRight}from"lucide-react";functionCustomButton({ onClick, direction }){return(<buttononClick={onClick}className="my-custom-btn">{direction==="left" ? <ChevronLeft/> : <ChevronRight/>}</button>);}<ScrollyButtonLeft={(props)=><CustomButton{...props}direction="left"/>}ButtonRight={(props)=><CustomButton{...props}direction="right"/>}>{/* children */}</Scrolly>;<ScrollyscrollSnapType="x proximity"scrollSnapAlign="start"gap={{0: "1rem",768: "1.5rem",1024: "2rem",}}paddingX={{0: "1rem",768: "2rem",1280: "4rem",}}scrollPaddingInline="1rem">{products.map((product)=>(<ProductCardkey={product.id}{...product}/>))}</Scrolly>Fully typed with TypeScript. The ResponsiveValue type allows you to pass either a single value or an object with breakpoint keys:
typeResponsiveValue<T>=T|Record<number,T>;The component uses CSS modules internally but exposes CSS custom properties for easy customization. Navigation buttons can be fully customized via the ButtonLeft and ButtonRight props.
Works in all modern browsers that support:
- CSS Container Queries
- CSS Scroll Snap
- Flexbox
MIT
Contributions welcome! Please open an issue or PR on GitHub.