- Notifications
You must be signed in to change notification settings - Fork214
A cross-platform Collapsible Tab View component for React Native
License
PedroBern/react-native-collapsible-tab-view
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
- Expo App
- Demo
- Features
- Installation
- Quick Start
- Guides
- API Reference
- Known Issues
- Alternative Libraries
- Contributing
React Native Collapsible Tab View is a versatile library for creating collapsible tab views usingReanimated.
- Explore theexamples for the source code of the Expo app.
Credits
Thereact-native-tab-view example app was used as a template for the demos.
| Default | Snap | revealHeaderOnScroll | revealHeaderOnScroll + Snap |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
- UI thread animations and interactions
- High customizability
- FullTypeScript support
- Lazy loading with fade-in animation
- DiffClamp header
- Interpolated header
- Scroll snap (with interpolated header)
- Animated snap (with diffClamp header)
- Scrollable tabs, inspired by thereact-native-tab-view tab bar
To install the library, open a terminal in your project's root directory and run:
yarn add react-native-collapsible-tab-view react-native-pager-view
Then, addReanimated,follow the official installation guide.
importReactfrom'react'import{View,StyleSheet,ListRenderItem}from'react-native'import{Tabs}from'react-native-collapsible-tab-view'constHEADER_HEIGHT=250constDATA=[0,1,2,3,4]constidentity=(v:unknown):string=>v+''constHeader=()=>{return<Viewstyle={styles.header}/>}constExample:React.FC=()=>{constrenderItem:ListRenderItem<number>=React.useCallback(({ index})=>{return(<Viewstyle={[styles.box,index%2===0 ?styles.boxB :styles.boxA]}/>)},[])return(<Tabs.ContainerrenderHeader={Header}headerHeight={HEADER_HEIGHT}// optional><Tabs.Tabname="A"><Tabs.FlatListdata={DATA}renderItem={renderItem}keyExtractor={identity}/></Tabs.Tab><Tabs.Tabname="B"><Tabs.ScrollView><Viewstyle={[styles.box,styles.boxA]}/><Viewstyle={[styles.box,styles.boxB]}/></Tabs.ScrollView></Tabs.Tab></Tabs.Container>)}conststyles=StyleSheet.create({box:{height:250,width:'100%',},boxA:{backgroundColor:'white',},boxB:{backgroundColor:'#D8D8D8',},header:{height:HEADER_HEIGHT,width:'100%',backgroundColor:'#2196f3',},})exportdefaultExample
To enable scrolling from the header, follow these steps:
- If the
HeaderComponentdoes not contain touchable components, set thepointerEventsprop to'none'. - If the
HeaderComponentdoes contain touchable components, set thepointerEventsprop to'box-none'to ensure they function properly.
Note: If any child component within theHeaderComponent shouldnot respond to touches, such as an<Image /> element, set itspointerEvents prop to'none'. Otherwise, it may unintentionally become the target of a touch gesture on iOS devices and prevent scrolling.
Basic usage looks like this:
import{Tabs}from'react-native-collapsible-tab-view'constExample=()=>{return(<Tabs.ContainerrenderHeader={MyHeader}><Tabs.Tabname="A"><ScreenA/></Tabs.Tab><Tabs.Tabname="B"><ScreenB/></Tabs.Tab></Tabs.Container>)}
| name | type | default | description |
|---|---|---|---|
allowHeaderOverscroll | boolean | undefined | false | Whether the header moves down during overscrolling (for example on pull-to-refresh on iOS) or sticks to the top |
cancelLazyFadeIn | boolean | undefined | ||
cancelTranslation | boolean | undefined | ||
containerStyle | StyleProp<ViewStyle> | ||
headerContainerStyle | StyleProp<AnimateStyle<ViewStyle>> | ||
headerHeight | number | undefined | Is optional, but will optimize the first render. | |
initialTabName | string | undefined | ||
lazy | boolean | undefined | If lazy, will mount the screens only when the tab is visited. There is a default fade in transition. | |
minHeaderHeight | number | undefined | Header minimum height when collapsed | |
onIndexChange | ((index: number) => void) | undefined | Callback fired when the index changes. It receives the current index. | |
onTabChange | (data: { prevIndex: number index: number prevTabName: T tabName: T }) => void | Callback fired when the tab changes. It receives the previous and current index and tabnames. | |
pagerProps | Omit<FlatListProps<number>, 'data' | 'keyExtractor' | 'renderItem' | 'horizontal' | 'pagingEnabled' | 'onScroll' | 'showsHorizontalScrollIndicator' | 'getItemLayout'> | Props passed to the pager. If you want for example to disable swiping, you can pass{ scrollEnabled: false } | |
renderHeader | (props: TabBarProps<TabName>) => React.ReactElement | null | ||
renderTabBar | (props: TabBarProps<TabName>) => React.ReactElement | null | (props: TabBarProps<TabName>) => MaterialTabBar | |
revealHeaderOnScroll | boolean | undefined | Reveal header when scrolling down. Implements diffClamp. | |
snapThreshold | number | null | undefined | null | Percentage of header height to define as the snap point. A number between 0 and 1, ornull to disable snapping. |
tabBarHeight | number | undefined | Is optional, but will optimize the first render. | |
width | number | undefined | Custom width of the container. Defaults to the window width. |
Wrap your screens withTabs.Tab. Basic usage looks like this:
<Tabs.Container...><Tabs.Tabname="A"label="First Tab"><ScreenA/></Tabs.Tab><Tabs.Tabname="B"><ScreenA/></Tabs.Tab></Tabs.Container>
| name | type |
|---|---|
label | string | ((props: TabItemProps<T>) => ReactNode) | undefined |
name | T |
Typically used internally, but if you want to mix lazy and regular screens you can wrap the lazy ones with this component.
| name | type |
|---|---|
cancelLazyFadeIn | boolean | undefined |
startMounted | boolean | undefined |
Use like a regular FlatList.
Use like a regular FlashList.
Use like a regular MasonryFlashList.
Use like a regular ScrollView.
Use like a regular SectionList.
You can pass a ref toTabs.Container.
constref=React.useRef()<Tabs.Containerref={ref}>
| method | type |
|---|---|
| jumpToTab | (name: T) => boolean |
| setIndex | (index: number) => boolean |
| getFocusedTab | () => T |
| getCurrentIndex | () => number |
This hook provides access to key styles for the collapsible tab view. It can be used to obtain theprogressViewOffset and pass it to theRefreshControl of the scroll view.
const{ contentContainerStyle, progressViewOffset, style,}=useCollapsibleStyle()
| name | type |
|---|---|
| contentContainerStyle | { minHeight: number; paddingTop: number; } |
| progressViewOffset | number |
| style | { width: number; } |
This hook returns an animated value representing the current tab index. As the tab view can be in between panes while swiping, this value is a floating-point number.
consttabIndex=useAnimatedTabIndex()
This hook returns the name of the currently focused tab.
constfocusedTab=useFocusedTab()
This hook returns the top distance and the header height. For an example of how to use this, check out the animated header example in the example folder.
const{ top, height}=useHeaderMeasurements()
This hook returns the vertical scroll position of the current tab as an Animated SharedValue.
Since this library requires handling theonScroll event for its functionality, this is the only way to react to changes in the scroll position of the underlying scrollable component.
constscrollY=useCurrentTabScrollY()
Any additional props are passed to the pressable component.
| name | type | description |
|---|---|---|
activeColor | string | undefined | Color applied to the label when active |
inactiveColor | string | undefined | Color applied to the label when inactive |
inactiveOpacity | number | undefined | |
index | number | |
indexDecimal | SharedValue<number> | |
label | string | ((props: TabItemProps<T>) => ReactNode) | |
labelStyle | StyleProp<AnimateStyle<TextStyle>> | Style to apply to the tab item label |
name | T | |
onLayout | (((event: LayoutChangeEvent) => void) & ((event: LayoutChangeEvent) => void)) | undefined | Invoked on mount and layout changes with {nativeEvent: { layout: {x, y, width, height}}}. |
onPress | (name: T) => void | |
pressColor | string | undefined | |
pressOpacity | number | undefined | |
scrollEnabled | boolean | undefined | |
style | StyleProp<ViewStyle> | Either view styles or a function that receives a boolean reflecting whether the component is currently pressed and returns view styles. |
Refer tothis open issue. We utilizescrollTo to synchronize the unfocused tabs. While it is intended for use withScrollView, it works well withFlatList, until theRefreshControl is added. Note that this issue occurs only on Android.
Workaround: Check out theAndroid Shared Pull To Refresh example in the expo app. You can implement a single pull-to-refresh for theTabs.Container.
When using thestickyHeaderIndices prop on a FlatList orstickySectionHeadersEnabled on a SectionList, the sticky elements do not scroll up as the header collapses. This issue is specific to iOS.
See#136.
This is not an issue per se, but it's essential to be aware of it. When usingcontainerRef.current.setIndex(i), if you set it to the current index, the screen will scroll to the top. You can prevent this behavior as follows:
constindex=pageRef.current?.getCurrentIndex()if(index!==nextIndex){pageRef.current?.setIndex(nextIndex)}
If you do not require a full-featured tab view, consider another option: a simple segmented control / material tab bar without swiping or snapping, using only the React Native Animated API.
While developing, you can run theexample app to test your changes.
First runyarn in root:
yarn
Then prepare the example:
cd exampleyarnThen run the example:
yarn iosPlease follow theangular commit message format.
Make sure your code passes TypeScript and ESLint. Run the following to verify:
yarn typescriptyarn lint
To fix formatting errors, run the following:
yarn lint -- --fix
Edit theREADME_TEMPLATE, or update the docstrings inside thesrc folder, and run:
yarn docs
About
A cross-platform Collapsible Tab View component for React Native
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.



