Skip to content

CollapsibleTabView: React Native collapsible tabs API ​

CollapsibleTabView is the main component for building a native collapsible header, pinned tab bar and swipeable tab layout in React Native. It has a react-native-tab-view-like API and accepts a ref of type CollapsibleTabsRef. Start with Usage if you have not used it yet; the FlashList guide covers the list side.

tsx
import { CollapsibleTabView } from 'react-native-collapsible-tabs-native';

Layout ​

proptypenotes
navigationState{ index, routes }routes are { key, title } (react-native-tab-view shape)
renderScene({ route }) => ReactNodeone page per route; its body must pad by the header height, see createTabList
onIndexChange(index) => voidtab press or swipe settled. The pager settling on the already-selected index is not reported
renderHeader() => ReactNodethe collapsing header
renderTabBar({ routes, index, onIndexChange }) => ReactNodedefaults to TabBar. Want the tabs to scroll away with the header? Keep them here and pass pinTabBar={false}; don't move them into renderHeader (pages clear the tab-bar band's height either way, and an empty band gives the shell nowhere to put the tabs back without landing on content)
tabBarPropsTabBarPropscolours / onTabPress for the default TabBar; ignored with renderTabBar
lazybooleandefault true; mount a page on first visit (a page mounts as soon as it peeks into view during a swipe)
swipeEnabledbooleandefault true
styleViewStyleshell container style
refCollapsibleTabsRefimperative surface, see ref

Collapse ​

proptypenotes
collapseMode'classic' | 'direction''classic' (default): header returns as content nears the top. 'direction': any up-scroll reveals it, any down-scroll hides it
pinTabBarbooleandefault true: the tab bar stays pinned at the top once the header is gone. false: the whole band, tabs included, collapses as part of the header
headerMinHeightnumber (dp)default 0. Bottom strip of the header that stays pinned above the tab bar (a search bar, a filter row) instead of scrolling away. The tab bar necessarily stays too, so pinTabBar={false} is ignored while this is > 0
allowFullCollapsebooleandefault true. Tabs too short to scroll collapse the header anyway; native gives such a page exactly the scroll range it lacks; tabs with enough content are untouched. false restores the Twitter-style ease-back
collapseThresholdnumber (dp)crossing point for onCollapsedChange
onCollapsedChange(collapsed) => voidfires on crossings only, never per frame

Pull-to-refresh ​

proptypenotes
refreshing / onRefreshboolean / () => voidcontainer-level pull-to-refresh; keep refreshing true until done. Without onRefresh the pull gesture is not armed at all
refreshThresholdnumber (dp)pull distance that triggers a refresh on release. Default 70
refreshIndicatorOffsetnumber (dp)how far below the top the spinner rests while refreshing. Default 60
refreshTintColor / refreshBackgroundColorColorValuespinner colour, and the disc behind it (Android's native look, drawn on iOS too)
refreshIndicatorSize'default' | 'large'spinner size
refreshIndicatorHiddenbooleanhide the native spinner and draw your own from onHeaderOffsetChange's pull; the header band itself translates by the pull, so anything positioned above its top edge rides into view. iOS; on Android the spinner is invisible but there is no pull value. The gesture and onRefresh still fire

Per-frame events ​

Each accepts a Reanimated useEvent worklet or a plain (e) => void, and is emitted only while a handler is set. See Per-frame events for the worklet contract.

proppayloadnotes
onPageScroll{ position, offset }the pager's live swipe position, for a tab indicator that tracks the finger. The bundled TabBar already uses it: a plain function of yours is chained; a worklet takes the event over and the bundled indicator falls back to animating on settle
onHeaderOffsetChange{ offset, collapsibleHeight, pull } (dp)the bands' live offset while they move; offset / collapsibleHeight is the 0..1 progress. For a header that reacts to its own collapse (avatar shrink, title fade, cover parallax). Emitted only on change
onScrollOffsetChange{ index, offset } (dp)the active list's live scroll offset from its content top (0 = header open, collapsibleHeight = collapsed, larger = scrolled on; negative on an over-drag), per frame while it moves and once when the active page changes. For parallax deeper in the page, a scroll-to-top pill, a progress bar

Types ​

ts
import type {
  CollapsibleTabViewProps,
  CollapsibleTabsRef,
  Route,
  TabBarProps,
  PageScrollHandler,
  HeaderOffsetHandler,
  ScrollOffsetHandler,
} from 'react-native-collapsible-tabs-native';

Released under the MIT License.