Website • Examples
OverlayScrollbars
A javascript scrollbar plugin that hides the native scrollbars, provides custom styleable overlay scrollbars, and preserves the native functionality and feel.
Why?
I created this plugin because I hate ugly and space-consuming scrollbars. Similar plugins didn't meet my requirements in terms of features, quality, simplicity, license or browser support.
Goals & Features
- Simple, powerful and well-documented API.
- High browser compatibility - Firefox 59+, Chrome 55+, Opera 42+, Edge 15+ and Safari 10+.
- Fully Accessible - Native scrolling behavior is fully preserved.
- Can run on the server (
Node
,Deno
andBun
) - SSR, SSG and ISR support. - Tested on various devices - Mobile, Desktop and Tablet.
- Tested with various (and mixed) inputs - Mouse, Touch and Pen.
- Treeshaking - bundle only what you really need.
- Automatic update detection - no polling required.
- Leverage latest browser features - best performance in new browsers.
- Flow independent - supports all values for
direction
,flex-direction
andwriting-mode
. - Supports scroll snapping.
- Supports all virtual scrolling libraries.
- Supports the
body
element. - Easy and effective scrollbar styling.
- Highly customizable.
- TypeScript support - completely written in TypeScript.
- Dependency-free - 100% self-written to ensure small size and best functionality.
- High quality and fully typed framework versions for
react
,vue
,angular
,svelte
andsolid
.
Choose your framework
In addition to the vanilla JavaScript version, you can use the official framework components & utilities:
Getting started
npm & nodejs
OverlayScrollbars can be downloaded from npm or the package manager of your choice:
npm install overlayscrollbars
Once installed, it can be imported:
import 'overlayscrollbars/overlayscrollbars.css';
import {
OverlayScrollbars,
ScrollbarsHidingPlugin,
SizeObserverPlugin,
ClickScrollPlugin
} from 'overlayscrollbars';
Note: If the path
'overlayscrollbars/overlayscrollbars.css'
is not working use'overlayscrollbars/styles/overlayscrollbars.css'
as the import path for the CSS file.
You can use this Node Example as an reference / starting point.
Manual Download & Embedding
You can use OverlayScrollbars without any bundler or package manager.
Simply download one of the Releases or use a CDN.
- Use the javascript files with the
.browser
extension. - Use the javascript files with the
.es5
extension if you need to support older browsers, otherwise use the.es6
files. - For production use the javascript / stylesheet files with the
.min
extension.
Embed OverlayScrollbars manually in your HTML:
<link type="text/css" href="path/to/overlayscrollbars.css" rel="stylesheet" />
<script type="text/javascript" src="path/to/overlayscrollbars.browser.es.js" defer></script>
Use the global variable OverlayScrollbarsGlobal
to access the api similar to how you can do it in nodejs / modules:
var {
OverlayScrollbars,
ScrollbarsHidingPlugin,
SizeObserverPlugin,
ClickScrollPlugin
} = OverlayScrollbarsGlobal;
You can use this Browser Example as an reference or a starting point.
The examples in this documentation use the import
syntax instead of the OverlayScrollbarsGlobal
object. However, both versions are equivalent.
Initialization
The initialization of OverlayScrollbars
is explicit per element. Only the scrollbars of the element on which the plugin is initialized will be changed. Scrollbars of child elements will remain unchanged unless the plugin is initialized on them as well.
You can either initialize a new instance directly with an Element
or with an Object
where you have more control over the initialization process.
// Simple initialization with an element
const osInstance = OverlayScrollbars(document.querySelector('#myElement'), {});
Bridging initialization flickering
When you initialize OverlayScrollbars, it takes a few milliseconds to create and append all the elements to the DOM. During this time, the native scrollbars are still visible and will be switched out after the initialization is finished. This is seen as flickering.
To fix this behavior apply the data-overlayscrollbars-initialize
attribute to the target element (and the html
element as well when initializing a scrollbar for the body
element).
<!-- for the body element -->
<html data-overlayscrollbars-initialize>
<head></head>
<body data-overlayscrollbars-initialize></body>
</html>
<!-- for all other elements -->
<div data-overlayscrollbars-initialize>
OverlayScrollbars is applied to this div
</div>
Initialization with an Object
This is an in depth topic. Click here to read it.
The only required field is the target
field. This is the field to which the plugin will be applied.
If you use the object initialization with only the target
field, the result is equivalent to the element initialization:
// Both initializations have the same outcome
OverlayScrollbars(document.querySelector('#myElement'), {});
OverlayScrollbars({ target: document.querySelector('#myElement') }, {});
When initializing with an object you can specify how the library handles generated elements. For example, you can specify an existing element as the `viewport' element. Then the library won't generate it, but use the specified element instead:
OverlayScrollbars({
target: document.querySelector('#target'),
elements: {
viewport: document.querySelector('#viewport'),
},
}, {});
This is very useful if you have a fixed DOM structure and don't want OverlayScrollbars to create its own elements. These cases are very common when you want another library to work with OverlayScrollbars.
You can also decide to which element the scrollbars should be applied to:
OverlayScrollbars({
target: document.querySelector('#target'),
scrollbars: {
slot: document.querySelector('#target').parentElement,
},
}, {});
Last but not least, you can decide when to cancel the initialization:
OverlayScrollbars({
target: document.querySelector('#target'),
cancel: {
nativeScrollbarsOverlaid: true,
body: null,
}
}, {});
In the above example, the initialization will be aborted if the native scrollbars are overlaid, or if your target is a body
element and the plugin has determined that initializing to the body
element would interfere with native functionality such as window.scrollTo
.
Options
You can initialize OverlayScrollbars with an initial set of options, which can be changed at any time with the options
method:
OverlayScrollbars(document.querySelector('#myElement'), {
overflow: {
x: 'hidden',
},
});
Options in depth
This is an in depth topic. Click here to read it.
The default options are:
const defaultOptions = {
paddingAbsolute: false,
showNativeOverlaidScrollbars: false,
update: {
elementEvents: [['img', 'load']],
debounce: [0, 33],
attributes: null,
ignoreMutation: null,
},
overflow: {
x: 'scroll',
y: 'scroll',
},
scrollbars: {
theme: 'os-theme-dark',
visibility: 'auto',
autoHide: 'never',
autoHideDelay: 1300,
autoHideSuspend: false,
dragScroll: true,
clickScroll: false,
pointers: ['mouse', 'touch', 'pen'],
},
};
paddingAbsolute
type | default |
---|---|
boolean | false |
Indicates whether the padding for the content should be absolute.
showNativeOverlaidScrollbars
type | default |
---|---|
boolean | false |
Indicates whether the native overlaid scrollbars should be visible.
update.elementEvents
type | default |
---|---|
Array<[string, string]> | null | [['img', 'load']] |
An array of tuples. The first value in the tuple is an selector
and the second value are event names
. The plugin will update itself if any of the elements with the specified selector emits any of the specified events. The default value can be interpreted as "The plugin will update itself if any img
element emits a load
event."
update.debounce
type | default |
---|---|
[number, number] | number | null | [0, 33] |
Note: If 0 is used for the timeout,
requestAnimationFrame
will be used instead ofsetTimeout
for the debounce.
Debounces the MutationObserver
which tracks changes to the content. If a tuple is passed, the first value is the timeout and second is the max wait. If only a number it is treated as the timeout and there is no max wait. With null there is no debounce. Useful to fine-tune performance.
update.attributes
type | default |
---|---|
string[] | null | null |
Note: There is a base array of attributes that the
MutationObserver
always observes, even if this option isnull
.
An array of additional attributes that the MutationObserver
should observe the content for.
update.ignoreMutation
type | default |
---|---|
((mutation) => any) | null | null |
A function which receives a MutationRecord
as an argument. If the function returns a truthy value the mutation will be ignored and the plugin won't update. Useful to fine-tune performance.
overflow.x
type | default |
---|---|
string | 'scroll' |
Note: Valid values are:
'hidden'
,'scroll'
,'visible'
,'visible-hidden'
and'visible-scroll'
.
The overflow behavior for the horizontal (x) axis.
overflow.y
type | default |
---|---|
string | 'scroll' |
Note: Valid values are:
'hidden'
,'scroll'
,'visible'
,'visible-hidden'
and'visible-scroll'
.
The overflow behavior for the vertical (y) axis.
scrollbars.theme
type | default |
---|---|
string | null | 'os-theme-dark' |
Applies the specified theme (classname) to the scrollbars.
scrollbars.visibility
type | default |
---|---|
string | 'auto' |
Note: Valid values are:
'visible'
,'hidden'
, and'auto'
.
The visibility of a scrollbar if its scroll axis is able to have a scrollable overflow. (Scrollable overflow for an axis is only possible with the overflow behavior set to 'scroll'
or 'visible-scroll'
).
scrollbars.autoHide
type | default |
---|---|
string | 'never' |
Note: Valid values are:
'never'
,'scroll'
,'leave'
and'move'
.
Dictates whether to hide visible scrollbars automatically after a certain user action.
scrollbars.autoHideDelay
type | default |
---|---|
number | 1300 |
The delay in milliseconds before the scrollbars are automatically hidden.
scrollbars.autoHideSuspend
type | default |
---|---|
boolean | false |
Suspend the autoHide functionality until the first scroll interaction is performed.
The default value for this option is false
for backwards compatibility reasons but is recommended to be true
for better accessibility.
scrollbars.dragScroll
type | default |
---|---|
boolean | true |
Indicates whether you can drag the scrollbar handles for scrolling.
scrollbars.clickScroll
type | default |
---|---|
boolean | 'instant' | false |
Note: If set to
true
the ClickScrollPlugin is required.
Indicates whether you can click on the scrollbar track for scrolling.
scrollbars.pointers
type | default |
---|---|
string[] | null | ['mouse', 'touch', 'pen'] |
The PointerTypes
the plugin should react to.
TypeScript
// The options of a OverlayScrollbars instance.
type Options = {
// Whether the padding should be absolute.
paddingAbsolute: boolean;
// Whether to show the native scrollbars. Has effect only if the native scrollbars are overlaid.
showNativeOverlaidScrollbars: boolean;
// Customizes the automatic update behavior.
update: {
/**
* The given Event(s) from the elements with the given selector(s) will trigger an update.
* Useful for everything the MutationObserver and ResizeObserver can't detect
* e.g.: An image's `load` event or the `transitionend` / `animationend` events.
*/
elementEvents: Array<[elementSelector: string, eventNames: string]> | null;
/**
* The debounce which is used to detect content changes.
* If a tuple is provided you can customize the `timeout` and the `maxWait` in milliseconds.
* If a single number customizes only the `timeout`.
*