React-React custom scroll v4.4.0: Easily customize the browser scroll bar with native OS scroll behavior

icon
Latest Release: v4.4.0

NPM version

React-Custom-Scroll

An easily designable, cross browser (!!), custom scroll with ReactJS
Animations and scroll rate exactly like native scroll

See a working demo

Installation

npm i react-custom-scroll --save

Why do I need this ?

  • Same design on all browsers
  • Scrollbar is above the content instead of floating to the side - same layout on scrolled content as not scrolled content

How to use ?

Custom scroll component is available in commonJS format so you can just require it after installing.
There is also a UMD version - inside dist directory.
In both cases you have to include the customScroll.css file in your page.
It is located in /dist directory
From unpkg cdn:

Wrap your content with the custom scroll component
Remove any overflow style properties from your content root component - The custom scroll will take care of it

import CustomScroll from 'react-custom-scroll';
<CustomScroll>
  your content
</CustomScroll>

How to change the design ?

Your own custom design can be applied by styling these 2 classes in your css:

  • rcs-custom-scrollbar - this class styles the container of the scroll handle, you can use it if your handle width is greater than the default.
  • rcs-inner-handle - this class styles the handle itself, you can use it to change the color, background, border and such of the handle

You can see a usage example in example/firstComp/firstComp.scss

Options (react props)

  • allowOuterScroll : boolean, default false. Blocks outer scroll while scrolling the content
  • heightRelativeToParent : string, default undefined. Content height limit is relative to parent - the value should be the height limit.
  • flex : number, default undefined. If present will apply to the content wrapped by the custom scroll.
    This prop represents flex size. It is only relevant if the parent of customScroll has display: flex. See example below.
    This prop will override any value given to heightRelativeToParent when setting the height of customScroll.
  • onScroll - function, default undefined. Listener that will be called on each scroll.
  • addScrolledClass : boolean, default false. If true, will add a css class 'content-scrolled' while being scrolled.
  • freezePosition : boolean, default false. When true, will prevent scrolling.
  • minScrollHandleHeight : number, sets the mimimum height of the scroll handle. Default is 38, as in Chrome on OSX.
  • rtl : boolean, default false. Right to left document, will place the custom scrollbar on the left side of the content, and assume the native one is also there.
  • scrollTo: number, default undefined. Will scroll content to the given value.
  • keepAtBottom: boolean, default false. For dynamic content, will keep the scroll position at the bottom of the content, when the content changes, if the position was at the bottom before the change. See example here
Example for heightRelativeToParent
<CustomScroll heightRelativeToParent="calc(100% - 20px)">
  your content
</CustomScroll>  

It doesn't work, please help me

  • Check if you forgot to remove 'overflow' properties from the content root element.
  • If you're using JSX, make sure you use Pascal case and not camelCase <CustomScroll> and not <customScroll>.
    starting with lower case causes JSX to treat the tag as a native dom element
  • Make sure you have a height limit on the content root element (max-height)
  • Check if your height limit is relative to parent, and you didn't use heightRelativeToParent prop.

Usage with flexbox

See a demo with Flex

There are some details that apply when using customScroll on elements with size set by css flex.
Here is an example for an HTML structure before using customScroll:

<SomeParent style="display: flex; height: 500px;">
  <FixedHeightElement style="height: 100px"><FixedHeightElement />
  <FlexibleHeightElement style="flex:1; overflow:scroll">
    your content (with enough height to cause a scroll)
  <FlexibleHeightElement />
</SomeParent>  

In this example, a scroll is active on the flexibleHeightElement, where the flex size sets it's height to 400px, after the fixedHeight element took 100px.

Solutions

There are 2 options to use customScroll with this structure:

  • Wrapping the content:
    For this solution, the overflow property should be removed from the flex size element, since the customScroll will take care of that. Instead, min-height and min-width should be set to 0.
<someParent style="display: flex; height: 500px;">
  <fixedHeightElement style="height: 100px"><fixedHeightElement/>
  <flexibleHeightElement style="flex:1; min-height: 0; min-width: 0">
    <CustomScroll heightRelativeToParent="100%">
      your content (with enough height to cause a scroll)
    <CustomScroll/>
  <flexibleHeightElement/>
</someParent>  

min-height and min-width are required since flex won't shrink below it's minimum content size (flex box spec).

  • Replacing the flex-size element with customScroll
<someParent style="display: flex; height: 500px;">
  <fixedHeightElement style="height: 100px"><fixedHeightElement/>
  <CustomScroll flex="1">
      your content (with enough height to cause a scroll)
  <CustomScroll/>
</someParent>  

Contributing

To build the project in watch mode, run 'npm run develop' or 'yarn develop'.
For production build - run yarn build .

Tests

npm install
npm test
# Or for continuous run
npx karma start

Comments

  • reactCustomScroll.js:289 Uncaught TypeError: Super expression must either be null or a function
    reactCustomScroll.js:289 Uncaught TypeError: Super expression must either be null or a function

    Jun 15, 2020

    the following error only on PROD deployment, on apache web server and also on azure app service deployment. image

    Works fine with local deployment with npm/ yarn start

    What could be the issue?

    Reply
  • Always show scrollbar
    Always show scrollbar

    Jun 19, 2020

    Hi, is there any method to do scrollbar always visible ? (not only on hovering with mouse for macOs)

    Reply
  • fix ie compatible issue
    fix ie compatible issue

    Jul 16, 2020

    fix event is not removed correctly in IE 11

    Reply
  • prop-types version dependency for scrollbar
    prop-types version dependency for scrollbar

    Oct 30, 2020

    While using this package if we do not specify prop-type (15.0.0) as peer dependency, we are seeing this error, and it is forcing us to include that as peer dependency in our project, can we remove it from peer dependency?

    src/index.jsx → dist/index.cjs.js, dist/index.esm.js...
    babelHelpers: 'bundled' option was used by default. It is recommended to configure this option explicitly, read more here: https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers
    [!] Error: 'oneOf' is not exported by node_modules/prop-types/index.js, imported by node_modules/react-scrollbars-custom/dist/rsc.esm.js
    https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
    node_modules/react-scrollbars-custom/dist/rsc.esm.js (2:9)
    1: import { cnb } from 'cnbuilder';
    2: import { oneOf, func, bool, number, string, object } from 'prop-types';
                ^
    3: import { createElement, createRef, Component, createContext } from 'react';
    4: import { zoomLevel } from 'zoom-level';
    Error: 'oneOf' is not exported by node_modules/prop-types/index.js, imported by node_modules/react-scrollbars-custom/dist/rsc.esm.js
        at error (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:5251:30)
        at Module.error (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:9810:16)
        at handleMissingExport (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:9699:28)
        at Module.traceVariable (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:10200:24)
        at ModuleScope.findVariable (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:8731:39)
        at Identifier$1.bind (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:4137:40)
        at CallExpression$1.bind (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:2867:23)
        at CallExpression$1.bind (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:6628:15)
        at VariableDeclarator.bind (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:2867:23)
        at VariableDeclaration.bind (/var/jenkins/workspace/react-foundation_14_63014_1/node_modules/rollup/dist/shared/rollup.js:2863:31)
    
    error Command failed with exit code 1.
    info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
    [Pipeline] }
    
    
    Reply
  • rcs-custom-scrollbar / scrollbar does not show up
    rcs-custom-scrollbar / scrollbar does not show up

    Jan 30, 2021

    Hello,

    the scrollbar seems not showed up.

    Screenshot 2021-01-30 at 21 48 41

    Here is my code:

    Scrollbar.tsx

    import React from 'react'
    import CustomScroll from 'react-custom-scroll'
    
    const Scrollbar: React.FC<{
        children: React.ReactNode
    }> = ({ children }) => {
        return <CustomScroll>{children}</CustomScroll>
    }
    
    export default Scrollbar
    

    ModalContent.tsx:

    import Scrollbar from '@/components/Scrollbar'
    import { styled } from 'linaria/react'
    import React from 'react'
    
    const ModalContent: React.FC<{ children: React.ReactNode }> = ({ children }) => {
        return (
            <Container>
                <Scrollbar>{children}</Scrollbar>
            </Container>
        )
    }
    
    export default ModalContent
    
    const Container = styled.div`
        max-height: 50vh;
        overflow-x: hidden;
    `
    

    I have already hide the native scrollbar by the following css:

    *{
      -ms-overflow-style: none !important;
       scrollbar-width: none !important;
    }
    ::-webkit-scrollbar {
        display: none !important;
        width: 0 !important;
        background: transparent;
    }
    
    Reply
  • feat(drag): add drag start/end handlers
    feat(drag): add drag start/end handlers

    Mar 2, 2021

                                                                                                                                                                                                           
    Reply
  • Add persistentScrollbar props, example, test and doc
    Add persistentScrollbar props, example, test and doc

    May 17, 2021

    PR from issue #88

    Added persistentScrollbar as a boolean prop.

    If this PR gets merged, I will update types repo of this package too.

    @rommguy

    Reply
  • Bump path-parse from 1.0.6 to 1.0.7
    Bump path-parse from 1.0.6 to 1.0.7

    Aug 10, 2021

    Bumps path-parse from 1.0.6 to 1.0.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    Reply
  • blockOuterScroll behaviour - scroll is jumping
    blockOuterScroll behaviour - scroll is jumping

    Aug 18, 2021

    Hello!

    Scroll is jumping sometimes in our portal. I tried to investigate what causes this behaviour.

    https://github.com/rommguy/react-custom-scroll/blob/1f4757198e2478090ac21bcd2849ae388f3afc60/src/main/customScroll.js#L277

    Changing this line to const delta = e.deltaY can fix this behaviour.

    Please explain what is behind this mathematics(% 3 and * 10).

    Reply
  • warning [email protected].3.0" has unmet peer dependency "[email protected]*"." class="image-fit lazy">
    warning " > [email protected]" has unmet peer dependency "[email protected]*".

    Nov 30, 2021

    I'm using Typescript to develop react project. So, prop-types is useless for me, but the warning is so annoying.

    image

    Reply
  • Is there scrolltoBottom option?
    Is there scrolltoBottom option?

    Oct 6, 2016

    How to acheive the scrolltoBottom option when the content area is dynamically increased,

    Reply
  • React 15.5 warnings
    React 15.5 warnings

    Apr 26, 2017

    Will you update to support react 16? React 15.5 is firing the deprecation warnings of proptype and createClass. More info: React v15.5.0

    Reply
  • Chrome Mouse Wheel preventDefault problem
    Chrome Mouse Wheel preventDefault problem

    Sep 26, 2019

    using chrome, on mouse wheel scroll, i'm getting an error '[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive'. If we remove line 288 and 291 in blockOuterScroll function within customScroll.js the problem seems to be fixed. Chrome v #77

    Reply
  • Fix scroll thumb visibility on mobile
    Fix scroll thumb visibility on mobile

    Jul 5, 2018

    This PR makes scroll thumb visible when scrolling on mobile device. Also it adds native kinetic scroll on mobile devices.

    Reply
  • Uncaught TypeError on click
    Uncaught TypeError on click

    May 18, 2018

    When clicking in a scroll-able area I get the following error:

    Uncaught TypeError: t.getBoundingClientRect(...).toJSON is not a function
        at t.value (reactCustomScroll.js?:formatted:198)
        at t.value (reactCustomScroll.js?:formatted:186)
        at HTMLUnknownElement.callCallback (react-dom.development.js:542)
        at Object.invokeGuardedCallbackDev (react-dom.development.js:581)
        at Object.invokeGuardedCallback (react-dom.development.js:438)
        at Object.invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:452)
        at executeDispatch (react-dom.development.js:836)
        at executeDispatchesInOrder (react-dom.development.js:858)
        at executeDispatchesAndRelease (react-dom.development.js:956)
        at executeDispatchesAndReleaseTopLevel (react-dom.development.js:967)
    

    Caused by toJSON() being called on the result of getBoundingClientRect() in the isMouseEventOnCustomScrollbar function called from the onClick event handler.

    value: function(e) {
                        if (!this.customScrollbarRef)
                            return !1;
                        var t = p.findDOMNode(this)
                          , o = t.getBoundingClientRect().toJSON() <-- here!
                          , n = this.customScrollbarRef.getBoundingClientRect()
                          , r = this.props.rtl ? {
    
    Reply
  • 2 scrollbars on MacOS Chrome #2
    2 scrollbars on MacOS Chrome #2

    Mar 1, 2018

    Hi all, I have the same problem on my macOS with Chrome, which was mentioned in #10 by @dnish

    I checked and removed all overflow properties on parent blocks, add the max-height rule, but still have 2 scrolls.

    code:

    <CustomScroll heightRelativeToParent="calc(100%)">
        {this.renderList(predictions)}
    </CustomScroll>
    

    here is an online demo, look at Predictions block.

    untitled

    What I do wrong?

    Reply
  • Flexbox and scrolling
    Flexbox and scrolling

    Jun 19, 2016

    Hey, just fixed some div's of the other issue, now I'm trying to add custom scroll to a flexbox layout. For example, we are having the following structure:

    <div id="chat">
      <div class="header"></div>
      <div class="messages"><CustomScroll heightRelativeToParent="100%">....</CustomScroll></div>
       <div class="write"></div>
    </div>
    

    CSS:

    #chat {
     display: flex;
     flex-direction:column;
    }
    
    .header {
      height: 60px;
     }
    
    .messages {
      flex: 1;
      overflow-y:hidden;
    }
    
    .writer {
     height: 50px;
     }
    

    If I start scrolling, I've 2 scrollbars within my messages div. The one of CustomScroll and the native one. I think that overflow-y causes the issue, but it's needed because of flex:1;. Any idea how I could fix this issue?

    Reply
  • keepAtBottom doesn't work if the size of the inner element doesn't change
    keepAtBottom doesn't work if the size of the inner element doesn't change

    Nov 6, 2017

    Scenario: I have a chatcomponent that shows only the latest 50 chats in a conversation. If all my chat lines have the same height and a new line is added (which causes the previous line 0 to disappear and adds a new line on index 49), the component doesn't scroll down.

    I've tried editing this line so that it compares this.props.children with prevProps.children, but doesn't always work because for some reason the reachedBottomOnCurrentRender evaluates to false..

    Any idea?

    Currently I just copied your code and removed the reachedBottomOnCurrentRender which still makes the component behave as expected, so not sure what the use of that check is?

    bug 
    Reply
  • Failure to Compile
    Failure to Compile

    Jun 20, 2020

    image

    These are my dependencies for package.json "@date-io/date-fns": "^1.3.13", "@material-ui/core": "^4.8.0", "@material-ui/icons": "^4.5.1", "@material-ui/pickers": "^3.2.8", "axios": "^0.19.0", "bootstrap": "^4.4.1", "date-fns": "^2.8.1", "draft-js": "^0.11.5", "draft-js-export-html": "^1.4.1", "node-sass": "^4.14.1", "react": "^16.12.0", "react-app-polyfill": "^1.0.6", "react-custom-scroll": "^4.2.0", "react-dom": "^16.12.0", "react-router-dom": "^5.1.2", "react-scripts": "^3.4.1", "react-select": "^3.0.8", "react-transition-group": "^4.4.1", "reactstrap": "^8.2.0", "typeface-roboto": "0.0.54"

    Reply
  • Class names should be more unique or assignable
    Class names should be more unique or assignable

    Jun 26, 2018

    I would advise changing class names that are used to something more unique. Or even make them customisable. For example these are pretty common and can be already used in any kind of a project: outer-container, inner-container, content-wrapper.

    That's what I encountered. The script just doesn't work because those already have assigned height, width, etc.

    Reply