After perusing the React Native Documentation I couldn't seem to find out how to make a <ScrollView>
have a persistent scrollbar that doesn't fade out. How would I achieve that?

- 6,127
- 6
- 22
- 23
-
have you found solution? – stkvtflw Jun 10 '16 at 18:01
-
I'm wondering the same! – Kyle Erickson Oct 26 '16 at 17:56
-
any solution? at the moment I have set theme styles to `
- always
` but this is not proper solution. – Pir Shukarullah Shah Feb 02 '17 at 15:05 -
I need that too! – SudoPlz Mar 24 '17 at 16:59
-
3I've added a feature request for react-native (this is where they officially recommend putting feature requests per the repo): https://react-native.canny.io/feature-requests/p/option-to-always-show-scroll-indicator-on-scrollview – jwinn Jun 13 '17 at 13:19
-
anything? on this :( – wdfc Sep 20 '17 at 18:25
-
May be an explanation for the lack of support: it's discouraged by Apple isn't and requires a hack to get working on native iOS: https://stackoverflow.com/a/15613852/2083679 – Rob Hogan Nov 04 '17 at 22:54
-
You cannot. And it's not in the react-native roadmap – sospedra Dec 19 '17 at 13:52
3 Answers
iOS
The underlying iOS native component, UIScrollView
(technically, RCTEnhancedScrollView
), doesn't support keeping the scroll indicators visible. For this reason, the React Native wrapper around it won't either.
There is a hack to get this working with the native component (see this answer for one approach). To accomplish this in React Native, you'd need to implement this hack on the native side, and then either create your own Native Module or fork React Native and modify their ScrollView component.
That said, the iOS Scroll View interface guidelines discourage this, so you may want to leave the indicators' behavior alone.
Android
A few approaches:
- set
<item name="android:overScrollMode">always</item>
, - set
android:fadeScrollbars="false"
in XML, or - set
ScrollView.setScrollbarFadingEnabled(false)
in Java (e.g. in your custom native bridge code)
This is similarly discouraged as nonstandard UI unless you have a strong reason for it.

- 65,323
- 19
- 161
- 287
-
Android's native WebView has a scrollbarFadingEnabled option that you could set to false to always show the scrollbar – Mark Z. Aug 16 '19 at 13:20
-
@MarkZhukovsky Thanks! I updated the answer with a few more approaches including the one you mentioned. – Aaron Brager Aug 17 '19 at 01:59
Adding answer since none of the above worked for me.
Android now has the persistentScrollbar props.
iOS does not support this. So I created a JS solution that can be used as follows:
<SBScrollView persistentScrollbar={true}>...</SBScrollView>
Basically, this functional component will use persistentScrollbar when on Android, while add a bar when we are on iOS. It is not smooth for now, but it is functional.
// @flow
import React, {useState} from 'react';
import {Platform, View, ScrollView} from 'react-native';
type Props = {|
persistentScrollbar?: boolean,
children?: React$Node,
|} & View.propTypes;
export default function SBScrollView({
persistentScrollbar = false,
children,
...other
}: Props) {
const [nativeEvent, setNativeEvent] = useState();
if (Platform.OS === 'android' || !persistentScrollbar) {
// Abdroid supports the persistentScrollbar
return (
<ScrollView persistentScrollbar={persistentScrollbar} {...other}>
{children}
</ScrollView>
);
}
const top = nativeEvent
? nativeEvent.contentOffset.y +
(nativeEvent.contentOffset.y / nativeEvent.contentSize.height) *
nativeEvent.layoutMeasurement.height
: 0;
// iOS does not support persistentScrollbar, so
// lets simulate it with a view.
return (
<ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={event => setNativeEvent(event.nativeEvent)}
{...other}>
{children}
<View
style={{
position: 'absolute',
top,
right: 4,
height: 200,
width: 4,
borderRadius: 20,
backgroundColor: 'gray',
}}
/>
</ScrollView>
);
}
I hope this can help others.

- 7,023
- 2
- 49
- 58
I was looking for a solution but I didn't find nothing, then I created a solution, I hope can help you with it. I created a view View with height and width and put it over my scrollview, after that I used the Props of scrollview like onMomentumScrollBegin, onMomentumScrollEnd, onContentSizeChange and onScroll
after that I make a condition with a boolean variable, if this variable is false, the View is visible, if is false the View is hide, How do I active this variable? with the Prop onMomentumScrollBegin that detect when you use the scrollView and the same way to set the variable in false with onMomentumScrollEnd that detects when the scroll ends.
The Prop onContentSizeChange allows me to get the height and width of my scrollview, this values I used to calculate where would be set the scrollbar/scrollIndicator
and finally with the Prop onScroll I get the position.
the example:
<ScrollView
onMomentumScrollBegin={() => {this.setvarScrollState()}}
onMomentumScrollEnd={() => {this.setvarScrollStateRev()}}
scrollEventThrottle={5}
onContentSizeChange={(w, h) => this.state.hScroll = h}
showsVerticalScrollIndicator={false}
onScroll={event => { this.state.wScroll = event.nativeEvent.contentOffset.y }}
style={{ marginVertical: 15, marginHorizontal:15, width: this.state.measurements3.width}}>
{
Mydata.map((value, index) => {
return <TouchableOpacity>
<Text>{ value.MyDataItem }</Text>
</TouchableOpacity>
})
}
the functions:
setvarScrollState() {
this.setState({VarScroll: true});
}
setvarScrollStateRev() {
this.setState({VarScroll: false});
}
and the variable
this.state = {VarScroll: false}
Then my condition is
!this.state.VarScroll ?
<View
style = {{
marginTop: 200*(this.state.wScroll / this.state.hScroll),
marginLeft:338.5,
height: 35,
width: 2,
backgroundColor: 'grey',
position:'absolute'
}}
/>
: null
Why 200? because is the maximum value that my marginTop can set
Check the picture
Final note: the scrollView have to be inside a View with the another View (scrollbar) something like this
<View>
{/*---- ScrollBar and conditions----*/}
<View>
<View>
<ScrollView>
</ScrollView>
</View>

- 196
- 1
- 12