Collapsible tab view component for React Native
npm install rn-collapsible-tab-viewCloned from https://github.com/PedroBern/react-native-collapsible-tab-view and adapted to allow horizontal scroll and touchables at the top container element.
To reduce the complexity and time to implement it the snap and header overscroll feature were removed.
- Expo app
- Demo
- Features
- Installation
- Quick Start
- Guides
- Scroll on header
- API reference
- Core
- Tabs.Container
- Tabs.Lazy
- Tabs.FlatList
- Tabs.SectionList
- Tabs.ScrollView
- Ref
- Hooks
- useCollapsibleStyle
- useAnimatedTabIndex
- useFocusedTab
- useHeaderMeasurements
- Default Tab Bar
- MaterialTabBar
- MaterialTabItem
- Known issues
- Android FlatList pull to refresh
- iOS FlatList/ SectionList stickyHeaders
- ref.setIndex
- Alternative libraries
- Contributing
- Documentation changes
Credits
The react-native-tab-view example app was used as template for the demos.
| Default | |
| :-----: | |
| 
- Animations and interactions on the UI thread
- Highly customizable
- Fully typed with TypeScript
- Lazy support with fade-in animation
- Interpolated header
- Scrollable tabs, inspired by the react-native-tab-view tab bar
- Support horizontal and vertical window
Open a Terminal in the project root and run:
``sh`
yarn add react-native-collapsible-tab-view@next
Then, add Reanimated v2, follow the official installation guide.
`tsx
import React from 'react'
import { View, StyleSheet, ListRenderItem } from 'react-native'
import { Tabs } from 'react-native-collapsible-tab-view'
const HEADER_HEIGHT = 250
const DATA = [0, 1, 2, 3, 4]
const identity = (v: unknown): string => v + ''
const Header = () => {
return
}
const Example: React.FC = () => {
const renderItem: ListRenderItem
return (
)
}, [])
return (
headerHeight={HEADER_HEIGHT} // optional
>
renderItem={renderItem}
keyExtractor={identity}
/>
)
}
const styles = StyleSheet.create({
box: {
height: 250,
width: '100%',
},
boxA: {
backgroundColor: 'white',
},
boxB: {
backgroundColor: '#D8D8D8',
},
header: {
height: HEADER_HEIGHT,
width: '100%',
backgroundColor: '#2196f3',
},
})
export default Example
`
Basic usage looks like this:
`tsx
import { Tabs } from 'react-native-collapsible-tab-view'
const Example = () => {
return (
)
}
`
#### Props
| name | type | default | description |
| :------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: |
| cancelLazyFadeIn | boolean \| undefined | | |boolean \| undefined
| cancelTranslation | | | |StyleProp
| containerStyle | | | |StyleProp
| headerContainerStyle | | | |number \| undefined
| headerHeight | | | Is optional, but will optimize the first render. |string \| number \| undefined
| initialTabName | | | |boolean \| undefined
| lazy | | | If lazy, will mount the screens only when the tab is visited. There is a default fade in transition. |number \| undefined
| minHeaderHeight | | | Header minimum height when collapsed |((index: number) => void) \| undefined
| onIndexChange | | | Callback fired when the index changes. It receives the current index. |(data: { prevIndex: number index: number prevTabName: T tabName: T }) => void
| onTabChange | | | Callback fired when the tab changes. It receives the previous and current index and tabnames. |Omit
| pagerProps | | | Props passed to the horiztontal flatlist. If you want for example to disable swiping, you can pass { scrollEnabled: false } |(props: TabBarProps
| renderHeader | | | |(props: TabBarProps
| renderTabBar | | (props: TabBarProps | |number \| undefined
| tabBarHeight | | | Is optional, but will optimize the first render. |number \| undefined
| width | | | Custom width of the container. Defaults to the window width. |
Wrap your screens with Tabs.Tab. Basic usage looks like this:
`tsx`
#### Props
| name | type |
| :---: | :-------------------: |
| label | string \| undefined |T
| name | |
Typically used internally, but if you want to mix lazy and regular screens you can wrap the lazy ones with this component.
#### Props
| name | type |
| :--------------: | :--------------------: |
| cancelLazyFadeIn | boolean \| undefined |boolean \| undefined
| startMounted | |
Use like a regular FlatList.
Use like a regular ScrollView.
Use like a regular SectionList.
You can pass a ref to Tabs.Container.
`tsx`
const ref = React.useRef()
| method | type |
| :-------------: | :--------------------------: |
| jumpToTab | (name: T) => boolean |(index: number) => boolean
| setIndex | |() => T
| getFocusedTab | |() => number
| getCurrentIndex | |
Hook to access some key styles that make the whole think work. You can use this to get the progessViewOffset and pass to the refresh control of scroll view.
`tsx`
const {
contentContainerStyle,
progressViewOffset,
style,
} = useCollapsibleStyle()
#### Values
| name | type |
| :-------------------: | :------------------------------------------: |
| contentContainerStyle | { minHeight: number; paddingTop: number; } |number
| progressViewOffset | |{ width: number; }
| style | |
Returns an animated value representing the current tab index, as a floating point number.
`tsx`
const tabIndex = useAnimatedTabIndex()
Returns the currently focused tab name.
`tsx`
const focusedTab = useFocusedTab()
Returns the top distance and the header height. See the animated header example in the example folder.
`tsx`
const { top, height } = useHeaderMeasurements()
Any additional props are passed to the pressable component.
#### Props
| name | type | default | description |
| :-------------: | :------------------------------------------------------------------------------------------: | :-------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: |
| activeColor | string \| undefined | null | Color applied to the label when active |string \| undefined
| inactiveColor | | null | Color applied to the label when inactive |number \| undefined
| inactiveOpacity | | 0.7 | |number
| index | | | |SharedValue
| indexDecimal | | | |string
| label | | | |StyleProp
| labelStyle | | | Style to apply to the tab item label |T
| name | | | |(((event: LayoutChangeEvent) => void) & ((event: LayoutChangeEvent) => void)) \| undefined
| onLayout | | | Invoked on mount and layout changes with {nativeEvent: { layout: {x, y, width, height}}}. |(name: T) => void
| onPress | | | |string \| undefined
| pressColor | | #DDDDDD | |number \| undefined
| pressOpacity | | Platform.OS === 'ios' ? 0.2 : 1 | |boolean \| undefined
| scrollEnabled | | | |StyleProp
| style | | | Either view styles or a function that receives a boolean reflecting whether the component is currently pressed and returns view styles. |
See this open issue. We use scrollTo to synchronize the unfocused tabs, it's supposed to work only with ScrollView, but works great with FlatList, until the RefreshControl is added. Note that this happens only to android.
Workaround: see the Android Shared Pull To Refresh example in the expo app. You can have a single pull to refresh for the Tabs.Container.
When you use the stickyHeaderIndices prop on a FlatList or stickySectionHeadersEnabled on a SectionList, the sticky elements don't scroll up when the header collapses. This happens only on iOS.
See #136
This isn't an issue, but you need to know. When using containerRef.current.setIndex(i), if setting to the current index, the screen will scroll to the top. You can prevent this behavior like this:
`ts``
const index = pageRef.current?.getCurrentIndex()
if (index !== nextIndex) {
pageRef.current?.setIndex(nextIndex)
}