This is probably my favorite navigation pattern on Android, I wish it were more common on iOS! This is a very simple JavaScript-only implementation of it for React Native. For more information about how the animations behind this work, check out the Rebound section of the React Native Animation Guide
- Run 
npm install react-native-scrollable-tab-view --save var ScrollableTabView = require('react-native-scrollable-tab-view');
var ScrollableTabView = require('react-native-scrollable-tab-view'); var App = React.createClass({ render() { return ( <ScrollableTabView> <ReactPage tabLabel="React" /> <FlowPage tabLabel="Flow" /> <JestPage tabLabel="Jest" /> </ScrollableTabView> ); } }Suppose we had a custom tab bar called CustomTabBar, we would inject it into our ScrollableTabView like this:
var ScrollableTabView = require('react-native-scrollable-tab-view'); var CustomTabBar = require('./CustomTabBar'); var App = React.createClass({ render() { return ( <ScrollableTabView renderTabBar={() => <CustomTabBar someProp={'here'} />}> <ReactPage tabLabel="React" /> <FlowPage tabLabel="Flow" /> <JestPage tabLabel="Jest" /> </ScrollableTabView> ); } }Below is the default tab bar, renamed to CustomTabBar, you can use this as a template for implementing your own.
var React = require('react-native'); var { StyleSheet, Text, View, TouchableOpacity, } = React; var deviceWidth = require('Dimensions').get('window').width; var precomputeStyle = require('precomputeStyle'); var TAB_UNDERLINE_REF = 'TAB_UNDERLINE'; var styles = StyleSheet.create({ tab: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingBottom: 10, }, tabs: { height: 50, flexDirection: 'row', marginTop: 20, borderWidth: 1, borderTopWidth: 0, borderLeftWidth: 0, borderRightWidth: 0, borderBottomColor: '#ccc', }, }); var CustomTabBar = React.createClass({ propTypes: { goToPage: React.PropTypes.func, activeTab: React.PropTypes.number, tabs: React.PropTypes.array }, renderTabOption(name, page) { var isTabActive = this.props.activeTab === page; return ( <TouchableOpacity key={name} onPress={() => this.props.goToPage(page)}> <View style={[styles.tab]}> <Text style={{color: isTabActive ? 'navy' : 'black', fontWeight: isTabActive ? 'bold' : 'normal'}}>{name}</Text> </View> </TouchableOpacity> ); }, setAnimationValue(value) { this.refs[TAB_UNDERLINE_REF].setNativeProps(precomputeStyle({ left: (deviceWidth * value) / this.props.tabs.length })); }, render() { var numberOfTabs = this.props.tabs.length; var tabUnderlineStyle = { position: 'absolute', width: deviceWidth / numberOfTabs, height: 4, backgroundColor: 'navy', bottom: 0, }; return ( <View style={styles.tabs}> {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))} <View style={tabUnderlineStyle} ref={TAB_UNDERLINE_REF} /> </View> ); }, }); module.exports = CustomTabBar;renderTabBar(Function:ReactComponent) - should return a component to use as the tab bar. The component hasgoToPage,tabs,activeTabandrefadded to the props, and should implementsetAnimationValueto be able to animate itself along with the tab content.onChangeTab(Function) - function to call when tab changes, should accept 1 argument which is an Object containing two keys:i: the index of the tab that is selected,ref: the ref of the tab that is selectededgeHitWidth(Integer) - region (in pixels) from the left & right edges of the screen that can trigger swipe. Default is 30, which is the same as the swipe-back gesture on iOS.hasTouch(Function) - returnstruewhen ScrollableTabView starts being panned andfalsewhen it is released. Not triggered whenlocked(Bool) is true.locked(Bool) - dynamically disable scrolling between tabs.springTension(Integer) - a number between1and100, controls the amount of tension on the spring that guides the scroll animation - see example.springFriction(Integer) - a number between1and30, controls the amount of friction on the spring that guides the scroll animation - see example.clampSpring(Bool) - iftrue, the spring will not bounce at all - iffalse, it will oscillate around the target value before settling.children(ReactComponents) - each top-level child component should have atabLabelprop that can be used by the tab bar component to render out the labels. The default tab bar expects it to be a string, but you can use anything you want if you make a custom tab bar.
MIT Licensed

