@maximedemurger/tab-view
v0.3.3
Published
A react native component, support collapse header and custom refresh control, power by Reanimated v4 & GestureHandler V2.
Maintainers
Readme
@maximedemurger/tab-view
A high-performance React Native tab view component with collapsible header and custom refresh control.
Powered by React Native Reanimated v4 and GestureHandler v2.
Features
- 🎯 Smooth Collapsible Header - Seamless header collapse/expand animations
- 🔄 Custom Refresh Control - Pull-to-refresh with customizable behavior
- ⚡ High Performance - Built with Reanimated v4 for 60fps animations
- 📱 Cross-Platform - Support for iOS, Android, and Web
- 🎪 FlashList Support - Optimized for high-performance lists
- 🪝 React Hooks - Modern API using React Hooks
- 📦 TypeScript - Full TypeScript support with type definitions
- 🌊 Bounce Effect - iOS bounce effect on pull-to-refresh
- 🧩 Extensible - Easy to customize and extend
Requirements
Before installing this package, ensure you have:
- React Native 0.76+
- Expo SDK 54+ (for Expo projects)
- React 18.3+
Peer Dependencies
{
"react-native": ">=0.76",
"react-native-gesture-handler": ">=2.0.0",
"react-native-pager-view": ">=5.0.0",
"react-native-reanimated": ">=4.0.0",
"react-native-tab-view": ">3.3.0"
}Installation
Step 1: Install the package
npm install @maximedemurger/tab-view
# or
yarn add @maximedemurger/tab-viewStep 2: Install peer dependencies
If you don't have them already:
npm install react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-view
# or
yarn add react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-viewFor Expo projects:
expo install react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-viewStep 3: Setup (if needed)
For React Native projects, follow the setup instructions for:
Quick Start
import React, { useCallback, useState } from "react";
import { StatusBar, Text, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import {
TabView,
Route,
TabFlatList
} from "@maximedemurger/tab-view";
export function Example() {
const [isRefreshing, setIsRefreshing] = useState(false);
const [routes] = useState<Route[]>([
{ key: "tab1", title: "Feed", index: 0 },
{ key: "tab2", title: "Favorites", index: 1 },
{ key: "tab3", title: "Trending", index: 2 },
]);
const [index, setIndex] = useState(0);
const animationHeaderPosition = useSharedValue(0);
const animationHeaderHeight = useSharedValue(0);
const renderScene = useCallback(({ route }) => {
switch (route.key) {
case "tab1":
return (
<TabFlatList
index={route.index}
data={Array.from({ length: 20 })}
estimatedItemSize={60}
renderItem={({ index }) => (
<View
style={{
height: 60,
backgroundColor: "#fff",
marginBottom: 8,
justifyContent: "center",
paddingHorizontal: 16,
}}
>
<Text>Feed Item {index + 1}</Text>
</View>
)}
/>
);
case "tab2":
case "tab3":
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>{route.title}</Text>
</View>
);
default:
return null;
}
}, []);
const renderHeader = () => (
<View
style={{
height: 300,
backgroundColor: "#2196F3",
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={{ color: "#fff", fontSize: 24, fontWeight: "bold" }}>
Header
</Text>
</View>
);
const onStartRefresh = async () => {
setIsRefreshing(true);
// Simulate network request
await new Promise((resolve) => setTimeout(resolve, 1000));
setIsRefreshing(false);
};
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
renderScrollHeader={renderHeader}
minHeaderHeight={44 + (StatusBar.currentHeight || 0)}
onStartRefresh={onStartRefresh}
isRefreshing={isRefreshing}
animationHeaderPosition={animationHeaderPosition}
animationHeaderHeight={animationHeaderHeight}
lazy
/>
);
}API
TabView Component Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| navigationState | { index: number; routes: Route[] } | ✅ | Current tab state |
| renderScene | (props: SceneRendererProps) => ReactElement | ✅ | Render tab content |
| onIndexChange | (index: number) => void | ✅ | Called when tab changes |
| renderScrollHeader | () => ReactElement | ⭕ | Header component |
| minHeaderHeight | number | ⭕ | Minimum header height |
| onStartRefresh | () => Promise<void> | ⭕ | Called on pull-to-refresh |
| isRefreshing | boolean | ⭕ | Is currently refreshing |
| animationHeaderPosition | Animated.SharedValue<number> | ⭕ | Header position animation |
| animationHeaderHeight | Animated.SharedValue<number> | ⭕ | Header height animation |
| lazy | boolean | ⭕ | Lazy load tabs (default: false) |
Scrollable Components
The package provides pre-built scrollable components:
TabFlatList- For flat lists with high performanceTabScrollView- For scroll viewsTabSectionList- For section lists
All support the same props as their React Native counterparts plus an index prop.
Compatibility
Version Support
- Expo SDK: 54+
- React Native: 0.76+
- React: 18.3+
- Reanimated: 4.0+
- GestureHandler: 2.0+
Platform Support
- ✅ iOS
- ✅ Android
- ✅ Web (via react-native-web)
Migration from v0.1.x
If you're upgrading from @showtime-xyz/tab-view v0.1.x, see MIGRATION.md for detailed instructions.
Examples
Check the example directory for more examples:
- Basic tab view with header
- Custom refresh control
- FlashList integration
- Advanced animations
To run the example:
cd example
yarn install
yarn startThen:
- iOS:
yarn ios - Android:
yarn android - Web:
yarn web
Performance Tips
- Use FlashList for large lists - it's more performant than FlatList
- Memoize callbacks with
useCallbackto prevent unnecessary re-renders - Use
lazyprop to defer rendering of off-screen tabs - Optimize images and assets
- Consider
React.memofor complex scene components
Troubleshooting
Header not collapsing?
- Ensure
renderScrollHeaderis provided - Check that
minHeaderHeightis set correctly - Verify
animationHeaderPositionandanimationHeaderHeightare shared values
Refresh control not working?
- Implement
onStartRefreshcallback - Set
isRefreshingstate properly - Ensure refresh is enabled on your scrollable component
Performance issues?
- Use
lazyprop to defer tab rendering - Replace FlatList with FlashList
- Profile with React DevTools
Contributing
Contributions are welcome! Please feel free to open an issue or submit a pull request.
License
MIT - See LICENSE for details
Credits
Originally created by Showtime
Maintained and updated for modern React Native by Digital Edge Studio
