Socket
Socket
Sign inDemoInstall

react-native-collapsible-tab-view

Package Overview
Dependencies
522
Maintainers
2
Versions
92
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    react-native-collapsible-tab-view

Collapsible tab view component for React Native


Version published
Weekly downloads
18K
increased by0.62%
Maintainers
2
Created
Weekly downloads
 

Readme

Source

React Native Collapsible Tab View

Build Status Version MIT License Runs with Expo

🚀 Version 6 released with Reanimated v3 support

React Native Collapsible Tab View is a versatile library for creating collapsible tab views using Reanimated.

  • Explore the examples for the source code of the Expo app.

Credits

The react-native-tab-view example app was used as a template for the demos.

Demo

DefaultSnaprevealHeaderOnScrollrevealHeaderOnScroll + Snap

Features

  • UI thread animations and interactions
  • High customizability
  • Full TypeScript 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 the react-native-tab-view tab bar

Installation

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, add Reanimated, follow the official installation guide.

Quick Start

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 <View style={styles.header} />
}

const Example: React.FC = () => {
  const renderItem: ListRenderItem<number> = React.useCallback(({ index }) => {
    return (
      <View style={[styles.box, index % 2 === 0 ? styles.boxB : styles.boxA]} />
    )
  }, [])

  return (
    <Tabs.Container
      renderHeader={Header}
      headerHeight={HEADER_HEIGHT} // optional
    >
      <Tabs.Tab name="A">
        <Tabs.FlatList
          data={DATA}
          renderItem={renderItem}
          keyExtractor={identity}
        />
      </Tabs.Tab>
      <Tabs.Tab name="B">
        <Tabs.ScrollView>
          <View style={[styles.box, styles.boxA]} />
          <View style={[styles.box, styles.boxB]} />
        </Tabs.ScrollView>
      </Tabs.Tab>
    </Tabs.Container>
  )
}

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

Guides

Scrolling on the Header

To enable scrolling from the header, follow these steps:

  • If the HeaderComponent does not contain touchable components, set the pointerEvents prop to 'none'.
  • If the HeaderComponent does contain touchable components, set the pointerEvents prop to 'box-none' to ensure they function properly.

Note: If any child component within the HeaderComponent should not respond to touches, such as an <Image /> element, set its pointerEvents prop to 'none'. Otherwise, it may unintentionally become the target of a touch gesture on iOS devices and prevent scrolling.

API Reference

Core

Tabs.Container

Basic usage looks like this:

import { Tabs } from 'react-native-collapsible-tab-view'

const Example = () => {
   return (
     <Tabs.Container renderHeader={MyHeader}>
       <Tabs.Tab name="A">
         <ScreenA />
       </Tabs.Tab>
       <Tabs.Tab name="B">
         <ScreenB />
       </Tabs.Tab>
     </Tabs.Container>
   )
}
Props
nametypedefaultdescription
allowHeaderOverscrollboolean | undefinedfalseWhether the header moves down during overscrolling (for example on pull-to-refresh on iOS) or sticks to the top
cancelLazyFadeInboolean | undefined
cancelTranslationboolean | undefined
containerStyleStyleProp<ViewStyle>
headerContainerStyleStyleProp<AnimateStyle<ViewStyle>>
headerHeightnumber | undefinedIs optional, but will optimize the first render.
initialTabNamestring | undefined
lazyboolean | undefinedIf lazy, will mount the screens only when the tab is visited. There is a default fade in transition.
minHeaderHeightnumber | undefinedHeader minimum height when collapsed
onIndexChange((index: number) => void) | undefinedCallback fired when the index changes. It receives the current index.
onTabChange(data: { prevIndex: number index: number prevTabName: T tabName: T }) => voidCallback fired when the tab changes. It receives the previous and current index and tabnames.
pagerPropsOmit<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
revealHeaderOnScrollboolean | undefinedReveal header when scrolling down. Implements diffClamp.
snapThresholdnumber | null | undefinednullPercentage of header height to define as the snap point. A number between 0 and 1, or null to disable snapping.
tabBarHeightnumber | undefinedIs optional, but will optimize the first render.
widthnumber | undefinedCustom width of the container. Defaults to the window width.

Tabs.Tab

Wrap your screens with Tabs.Tab. Basic usage looks like this:

<Tabs.Container ...>
  <Tabs.Tab name="A" label="First Tab">
   <ScreenA />
  </Tabs.Tab>
  <Tabs.Tab name="B">
   <ScreenA />
  </Tabs.Tab>
</Tabs.Container>
Props
nametype
labelstring | ((props: TabItemProps<T>) => ReactNode) | undefined
nameT

Tabs.Lazy

Typically used internally, but if you want to mix lazy and regular screens you can wrap the lazy ones with this component.

Props
nametype
cancelLazyFadeInboolean | undefined
startMountedboolean | undefined

Tabs.FlatList

Use like a regular FlatList.

Tabs.FlashList

Use like a regular FlashList.

Tabs.MasonryFlashList

Use like a regular MasonryFlashList.

Tabs.ScrollView

Use like a regular ScrollView.

Tabs.SectionList

Use like a regular SectionList.

Ref

You can pass a ref to Tabs.Container.

const ref = React.useRef()
<Tabs.Container ref={ref}>
methodtype
jumpToTab(name: T) => boolean
setIndex(index: number) => boolean
getFocusedTab() => T
getCurrentIndex() => number

Hooks

useCollapsibleStyle

This hook provides access to key styles for the collapsible tab view. It can be used to obtain the progressViewOffset and pass it to the RefreshControl of the scroll view.

const {
  contentContainerStyle,
  progressViewOffset,
  style,
} = useCollapsibleStyle()
Values
nametype
contentContainerStyle{ minHeight: number; paddingTop: number; }
progressViewOffsetnumber
style{ width: number; }

useAnimatedTabIndex

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.

const tabIndex = useAnimatedTabIndex()

useFocusedTab

This hook returns the name of the currently focused tab.

const focusedTab = useFocusedTab()

useHeaderMeasurements

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()

useCurrentTabScrollY

This hook returns the vertical scroll position of the current tab as an Animated SharedValue.

Since this library requires handling the onScroll event for its functionality, this is the only way to react to changes in the scroll position of the underlying scrollable component.

const scrollY = useCurrentTabScrollY()

Default Tab Bar

MaterialTabItem

Any additional props are passed to the pressable component.

Props
nametypedescription
activeColorstring | undefinedColor applied to the label when active
inactiveColorstring | undefinedColor applied to the label when inactive
inactiveOpacitynumber | undefined
indexnumber
indexDecimalSharedValue<number>
labelstring | ((props: TabItemProps<T>) => ReactNode)
labelStyleStyleProp<AnimateStyle<TextStyle>>Style to apply to the tab item label
nameT
onLayout(((event: LayoutChangeEvent) => void) & ((event: LayoutChangeEvent) => void)) | undefinedInvoked on mount and layout changes with {nativeEvent: { layout: {x, y, width, height}}}.
onPress(name: T) => void
pressColorstring | undefined
pressOpacitynumber | undefined
scrollEnabledboolean | undefined
styleStyleProp<ViewStyle>Either view styles or a function that receives a boolean reflecting whether the component is currently pressed and returns view styles.

Known Issues

Android FlatList Pull to Refresh

Refer to this open issue. We utilize scrollTo to synchronize the unfocused tabs. While it is intended for use with ScrollView, it works well with FlatList, until the RefreshControl is added. Note that this issue occurs only on Android.

Workaround: Check out the Android Shared Pull To Refresh example in the expo app. You can implement a single pull-to-refresh for the Tabs.Container.

iOS FlatList StickyHeaderIndices and iOS SectionList StickySectionHeadersEnabled

When using the stickyHeaderIndices prop on a FlatList or stickySectionHeadersEnabled on a SectionList, the sticky elements do not scroll up as the header collapses. This issue is specific to iOS.

See #136.

ref.setIndex

This is not an issue per se, but it's essential to be aware of it. When using containerRef.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:

const index = pageRef.current?.getCurrentIndex()
if (index !== nextIndex) {
  pageRef.current?.setIndex(nextIndex)
}

Alternative Libraries

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.

Contributing and running the Example

While developing, you can run the example app to test your changes.

First run yarn in root:

yarn

Then prepare the example:

cd example
yarn

Then run the example:

yarn ios

Please follow the angular commit message format.

Make sure your code passes TypeScript and ESLint. Run the following to verify:

yarn typescript
yarn lint

To fix formatting errors, run the following:

yarn lint -- --fix

Documentation changes

Edit the README_TEMPLATE, or update the docstrings inside the src folder, and run:

yarn docs

Keywords

FAQs

Last updated on 15 Apr 2024

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc