A lightweight, highly customizable React scrollbar component with TypeScript support, accessibility features, and cross-browser compatibility.
- 🪶 Lightweight: Only ~5.1 KB gzipped
- 🎨 Fully Customizable: Style with CSS or styled-components
- ♿ Accessible: Built-in ARIA support and keyboard navigation
- 📱 Cross-browser: Works on all modern browsers
- 🔒 TypeScript: Full TypeScript support with type definitions
- ⚡ Performant: Optimized for smooth scrolling experience
- 🎯 Zero Dependencies: No external runtime dependencies (except React)
- 🔄 Auto-hide: Scrollbars can hide when not in use
- 📏 Auto-height: Support for automatic height adjustment
- 🌍 Universal: SSR/SSG compatible
npm install @openabir/react-custom-scrollbar-lite
or
yarn add @openabir/react-custom-scrollbar-lite
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
function App() {
return (
<Scrollbar style={{ height: "400px", width: "300px" }}>
<div>
{/* Your scrollable content */}
<p>Long content that will be scrollable...</p>
<p>More content...</p>
<p>Even more content...</p>
</div>
</Scrollbar>
);
}
export default App;
Prop | Type | Default | Description |
---|---|---|---|
children |
ReactNode |
- | Content to be scrolled |
style |
CSSProperties |
{} |
Custom styles for the container |
className |
string |
- | CSS class name for the container |
autoHide |
boolean |
false |
Hide scrollbars when not scrolling |
autoHideTimeout |
number |
1000 |
Time in ms before hiding scrollbars |
autoHideDuration |
number |
200 |
Duration of hide animation (ms) |
thumbMinSize |
number |
30 |
Minimum size of scroll thumb in pixels |
universal |
boolean |
false |
Enable universal rendering (SSR) |
autoHeight |
boolean |
false |
Automatically adjust height to content |
autoHeightMin |
number | string |
0 |
Minimum height when autoHeight is enabled |
autoHeightMax |
number | string |
200 |
Maximum height when autoHeight is enabled |
hideTracksWhenNotNeeded |
boolean |
false |
Hide tracks when not needed |
renderThumbHorizontal |
(props: any) => ReactElement |
- | Custom horizontal thumb component |
renderThumbVertical |
(props: any) => ReactElement |
- | Custom vertical thumb component |
renderTrackHorizontal |
(props: any) => ReactElement |
- | Custom horizontal track component |
renderTrackVertical |
(props: any) => ReactElement |
- | Custom vertical track component |
renderView |
(props: any) => ReactElement |
- | Custom view component |
onScroll |
(event: Event) => void |
- | Scroll event handler |
onScrollFrame |
(values: ScrollValues) => void |
- | Called on each scroll frame |
onScrollStart |
() => void |
- | Called when scrolling starts |
onScrollStop |
() => void |
- | Called when scrolling stops |
onUpdate |
(values: ScrollValues) => void |
- | Called when scroll values update |
a11yEnabled |
boolean |
true |
Enable accessibility features |
ariaLabel |
string |
'Scrollable content' |
ARIA label for the scrollbar |
keyboardScrollAmount |
number |
40 |
Scroll amount (px) for keyboard navigation |
interface ScrollValues {
top: number; // Scroll top position (0-1)
left: number; // Scroll left position (0-1)
clientWidth: number; // Width of the view
clientHeight: number; // Height of the view
scrollWidth: number; // Total scrollable width
scrollHeight: number; // Total scrollable height
scrollLeft: number; // Current horizontal scroll position
scrollTop: number; // Current vertical scroll position
}
/* Container */
.custom-scrollbar {
/* Your styles */
}
/* Vertical track */
.custom-scrollbar .track-vertical {
background: rgba(0, 0, 0, 0.1);
width: 8px;
right: 2px;
bottom: 2px;
top: 2px;
border-radius: 4px;
}
/* Vertical thumb */
.custom-scrollbar .thumb-vertical {
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
cursor: pointer;
}
/* Horizontal track */
.custom-scrollbar .track-horizontal {
background: rgba(0, 0, 0, 0.1);
height: 8px;
left: 2px;
right: 2px;
bottom: 2px;
border-radius: 4px;
}
/* Horizontal thumb */
.custom-scrollbar .thumb-horizontal {
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
cursor: pointer;
}
import Scrollbar from "react-custom-scrollbar";
const CustomScrollbar = () => (
<Scrollbar
renderThumbVertical={({ style, ...props }) => (
<div {...props} style={{ ...style, backgroundColor: "#00f" }} />
)}
renderTrackVertical={({ style, ...props }) => (
<div {...props} style={{ ...style, backgroundColor: "#f0f0f0" }} />
)}
>
{/* Content */}
</Scrollbar>
);
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
const App = () => {
return (
<div style={{ width: 500, height: 300 }}>
<Scrollbar>
<div style={{ width: 1000, height: 1000 }}>
{/* Your content here */}
<p>Scrollable content</p>
</div>
</Scrollbar>
</div>
);
};
export default App;
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
const App = () => {
return (
<div style={{ width: 500, height: 300 }}>
<Scrollbar autoHide autoHideTimeout={1000} autoHideDuration={200}>
<div style={{ width: 1000, height: 1000 }}>
{/* Your content here */}
<p>Scrollable content with auto-hiding scrollbars</p>
</div>
</Scrollbar>
</div>
);
};
export default App;
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
const App = () => {
// Custom components for scrollbar parts
const renderThumbVertical = ({ style, ...props }) => {
const thumbStyle = {
...style,
backgroundColor: "#6e8efb",
borderRadius: 6,
};
return <div style={thumbStyle} {...props} />;
};
const renderTrackVertical = ({ style, ...props }) => {
const trackStyle = {
...style,
backgroundColor: "#f1f1f1",
borderRadius: 6,
};
return <div style={trackStyle} {...props} />;
};
return (
<div style={{ width: 500, height: 300 }}>
<Scrollbar
renderThumbVertical={renderThumbVertical}
renderTrackVertical={renderTrackVertical}
thumbMinSize={40}
>
<div style={{ width: 1000, height: 1000 }}>
{/* Your content here */}
<p>Scrollable content with custom styled scrollbars</p>
</div>
</Scrollbar>
</div>
);
};
export default App;
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
import { ScrollValues } from "@openabir/react-custom-scrollbar-lite";
const App = () => {
const handleScrollStart = () => {
console.log("Scrolling started");
};
const handleScrollStop = () => {
console.log("Scrolling stopped");
};
const handleScroll = (values: ScrollValues) => {
console.log("Scroll position:", values.top, values.left);
};
return (
<div style={{ width: 500, height: 300 }}>
<Scrollbar
onScrollStart={handleScrollStart}
onScrollStop={handleScrollStop}
onScroll={handleScroll}
>
<div style={{ width: 1000, height: 1000 }}>
{/* Your content here */}
<p>Scrollable content with scroll event handlers</p>
</div>
</Scrollbar>
</div>
);
};
export default App;
import React from "react";
import Scrollbar from "@openabir/react-custom-scrollbar-lite";
const App = () => {
return (
<div style={{ width: 500 }}>
<Scrollbar autoHeight autoHeightMin={100} autoHeightMax={500}>
<div>
{/* Your content here */}
<p>Content with auto-height scrollbar</p>
{/* More content... */}
</div>
</Scrollbar>
</div>
);
};
export default App;
The scrollbar component includes built-in accessibility features:
- ARIA attributes: Proper ARIA roles and properties
- Keyboard navigation: Arrow keys, Page Up/Down, Home/End
- Focus management: Proper focus handling for screen readers
- High contrast support: Respects OS high contrast settings
↑/↓
- Scroll vertically←/→
- Scroll horizontallyPage Up/Page Down
- Scroll by pageHome/End
- Scroll to start/end
<Scrollbar
a11yEnabled={true} // Enabled by default
ariaLabel="Custom scrollable content"
keyboardScrollAmount={60} // Custom scroll amount for keyboard navigation
>
<div>Content</div>
</Scrollbar>
This component is compatible with the following browsers:
Browser | Minimum Version |
---|---|
Chrome | 61+ |
Firefox | 60+ |
Safari | 12.1+ |
Edge | 79+ (Chromium-based) |
IE | Not supported |
Opera | 48+ |
iOS Safari | 12.2+ |
Android Chrome | 76+ |
The component uses modern browser APIs that might require polyfills for older browsers:
ResizeObserver
(Required for auto-resize functionality)- CSS Variables (Used for styling)
- Passive Event Listeners (Used for performance optimization)
For older browsers, you may need to include polyfills:
npm install resize-observer-polyfill
Then import and use the polyfill in your application entry point:
import { setupPolyfills } from "@openabir/react-custom-scrollbar-lite";
// Setup all polyfills
setupPolyfills();
The component supports SSR out of the box. For Next.js:
import dynamic from "next/dynamic";
const Scrollbar = dynamic(() => import("react-custom-scrollbar"), {
ssr: false,
});
- Use auto-hide: Reduces DOM updates when not scrolling
- Minimize re-renders: Use
React.memo
for content components - Optimize content: Use virtualization for large lists
- Throttle events: Throttle scroll event handlers if needed
Check out the examples directory for more usage examples:
Run the test suite:
npm test
Run tests with coverage:
npm run test:coverage
Run performance benchmarks:
npm run benchmark
See the benchmarks README for more information on performance testing.
We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
Created with ❤️ by openabir
See CHANGELOG.md for a list of changes and version history.
Found a bug? Please open an issue on GitHub.
Keywords: react, scrollbar, scroll, typescript, component, ui, accessibility, cross-browser, lightweight, customizable