| id | material-top-tab-navigator |
|---|---|
| title | Material Top Tabs Navigator |
| sidebar_label | Material Top Tabs |
A material-design themed tab bar on the top of the screen that lets you switch between different routes by tapping the tabs or swiping horizontally.
This navigator provides React Navigation integration for react-native-tab-view. If you don't need React Navigation integration, use react-native-tab-view directly instead.
To use this navigator, ensure that you have @react-navigation/native and its dependencies (follow this guide), then install @react-navigation/material-top-tabs:
npm install @react-navigation/material-top-tabsThe navigator depends on react-native-pager-view for rendering the pages.
If you have a Expo managed project, in your project directory, run:
npx expo install react-native-pager-viewIf you have a bare React Native project, in your project directory, run:
npm install react-native-pager-viewIf you're on a Mac and developing for iOS, you also need to install pods to complete the linking.
npx pod-install iosTo use this navigator, import it from @react-navigation/material-top-tabs:
import * as React from 'react';
import { Text, View } from 'react-native';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';
// codeblock-focus-start
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
// codeblock-focus-end
function HomeScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate('Profile')}>
Go to Profile
</Button>
</View>
);
}
function ProfileScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button onPress={() => navigation.navigate('Home')}>Go to Home</Button>
</View>
);
}
// codeblock-focus-start
const MyTabs = createMaterialTopTabNavigator({
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
// codeblock-focus-end
const Navigation = createStaticNavigation(MyTabs);
export default function App() {
return <Navigation />;
}In addition to the common props shared by all navigators, the material top tabs navigator component accepts the following additional props:
This controls what happens when goBack is called in the navigator. This includes pressing the device's back button or back gesture on Android.
It supports the following values:
firstRoute- return to the first screen defined in the navigator (default)initialRoute- return to initial screen passed ininitialRouteNameprop, if not passed, defaults to the first screenorder- return to screen defined before the focused screenhistory- return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the historynone- do not handle back button
Position of the tab bar in the tab view. Possible values are 'top' and 'bottom'. Defaults to 'top'.
String indicating whether the keyboard gets dismissed in response to a drag gesture. Possible values are:
'auto'(default): the keyboard is dismissed when the index changes.'on-drag': the keyboard is dismissed when a drag begins.'none': drags do not dismiss the keyboard.
Used to override default value of pager's overScroll mode.
Possible values:
'auto'(default): Allow a user to over-scroll this view only if the content is large enough to meaningfully scroll.'always': Always allow a user to over-scroll this view.'never': Never allow a user to over-scroll this view.
Only supported on Android.
Object containing the initial height and width of the screens. Passing this will improve the initial rendering performance. For most apps, this is a good default:
{
width: Dimensions.get('window').width,
}Style to apply to the tab view container.
Function that returns a React element to display as the tab bar.
Example:
import * as React from 'react';
import { Animated, View, Platform, Text } from 'react-native';
import {
createStaticNavigation,
useLinkBuilder,
useTheme,
} from '@react-navigation/native';
import { PlatformPressable } from '@react-navigation/elements';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
</View>
);
}
// codeblock-focus-start
function MyTabBar({ state, descriptors, navigation, position }) {
const { colors } = useTheme();
const { buildHref } = useLinkBuilder();
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name, route.params);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const inputRange = state.routes.map((_, i) => i);
const opacity = position.interpolate({
inputRange,
outputRange: inputRange.map((i) => (i === index ? 1 : 0.5)),
});
return (
<PlatformPressable
key={route.key}
href={buildHref(route.name, route.params)}
aria-label={options.tabBarAccessibilityLabel}
aria-selected={isFocused}
testID={options.tabBarButtonTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1, padding: 16, alignItems: 'center' }}
>
<Animated.Text style={{ opacity, color: colors.text }}>
{label}
</Animated.Text>
</PlatformPressable>
);
})}
</View>
);
}
const MyTabs = createMaterialTopTabNavigator({
tabBar: (props) => <MyTabBar {...props} />,
screens: {
Home: HomeScreen,
Settings: SettingsScreen,
},
});
// codeblock-focus-end
const Navigation = createStaticNavigation(MyTabs);
export default function App() {
return <Navigation />;
}This example will render a basic tab bar with labels.
Note that you cannot use the useNavigation hook inside the tabBar since useNavigation is only available inside screens. You get a navigation prop for your tabBar which you can use instead:
function MyTabBar({ navigation }) {
return (
<Button
onPress={() => {
// Navigate using the `navigation` prop that you received
// highlight-next-line
navigation.navigate('SomeScreen');
}}
>
Go somewhere
</Button>
);
}The following options can be used to configure the screens in the navigator:
Example:
import * as React from 'react';
import { Text, View } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
</View>
);
}
function ProfileScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
// codeblock-focus-start
const MyTabs = createMaterialTopTabNavigator({
// highlight-start
screenOptions: {
tabBarLabelStyle: { fontSize: 12 },
tabBarItemStyle: { width: 100 },
tabBarStyle: { backgroundColor: 'powderblue' },
},
// highlight-end
screens: {
Home: HomeScreen,
Settings: SettingsScreen,
Profile: ProfileScreen,
},
});
// codeblock-focus-end
const Navigation = createStaticNavigation(MyTabs);
export default function App() {
return <Navigation />;
}Generic title that can be used as a fallback for headerTitle and tabBarLabel.
Title string of a tab displayed in the tab bar or a function that given { focused: boolean, color: string } returns a React.Node, to display in tab bar. When undefined, scene title is used. To hide, see tabBarShowLabel option.
Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab.
Whether label font should scale to respect Text Size accessibility settings.
Whether the tab label should be visible. Defaults to true.
Function that given { focused: boolean, color: string } returns a React.Node, to display in the tab bar.
Whether the tab icon should be visible. Defaults to false.
Function that returns a React element to use as a badge for the tab.
Function that returns a React element as the tab bar indicator.
Style object for the tab bar indicator.
The indicator takes the same width as the tab item by default. It can be customized in a few ways:
- To make it smaller by a certain amount, you can add a horizontal margin, e.g.
{ marginHorizontal: 10 }. - Adding specific width will position the indicator at the left of the tab, e.g.
{ width: 20 }. - To center it in the tab when a custom width is specified, you can specify
marginas'auto', e.g.{ width: 20, marginHorizontal: 'auto' }.
When the tabStyle has width: 'auto' and no explicit width is specified for the indicator, it is scaled to the width with transform: [{ scaleX }] for smooth animations. So specifying a borderRadius won't work as expected.
If you need more control, you can use tabBarIndicator to render a custom indicator instead.
Style object for the view containing the tab bar indicator.
ID to locate this tab button in tests.
Color for the icon and label in the active tab.
Color for the icon and label in the inactive tabs.
Color for material ripple (Android >= 5.0 only).
Opacity for pressed tab (iOS and Android < 5.0 only).
Boolean indicating whether the tab bar bounces when overscrolling.
Boolean indicating whether to make the tab bar scrollable.
If you set this to true, you should also specify a width in tabBarItemStyle to improve the performance of initial render.
Style object for the tab label.
Style object for the individual tab items.
Style object for the view containing the tab items.
Style object for the tab bar.
Boolean indicating whether to enable swipe gestures. Swipe gestures are enabled by default. Passing false will disable swipe gestures, but the user can still switch tabs by pressing the tab bar.
Whether this screen should be lazily rendered. When this is set to true, the screen will be rendered as it comes into the viewport. By default all screens are rendered to provide a smoother swipe experience. But you might want to defer the rendering of screens out of the viewport until the user sees them. To enable lazy rendering for this screen, set lazy to true.
When you enable lazy, the lazy loaded screens will usually take some time to render when they come into the viewport. You can use the lazyPlaceholder prop to customize what the user sees during this short period.
When lazy is enabled, you can specify how many adjacent screens should be preloaded in advance with this prop. This value defaults to 0 which means lazy pages are loaded as they come into the viewport.
Function that returns a React element to render if this screen hasn't been rendered yet. The lazy option also needs to be enabled for this to work.
This view is usually only shown for a split second. Keep it lightweight.
By default, this renders null.
Style to apply to the view wrapping each screen. You can pass this to override some default styles such as overflow clipping.
The navigator can emit events on certain actions. Supported events are:
This event is fired when the user presses the tab button for the current screen in the tab bar. By default a tab press does several things:
- If the tab is not focused, tab press will focus that tab
- If the tab is already focused:
- If the screen for the tab renders a scroll view, you can use
useScrollToTopto scroll it to top - If the screen for the tab renders a stack navigator, a
popToTopaction is performed on the stack
- If the screen for the tab renders a scroll view, you can use
To prevent the default behavior, you can call event.preventDefault:
import * as React from 'react';
import { Text, View } from 'react-native';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
function HomeScreen() {
const navigation = useNavigation();
// codeblock-focus-start
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabPress', (e) => {
// Prevent default behavior
e.preventDefault();
// Do something manually
// ...
});
return unsubscribe;
}, [navigation]);
// codeblock-focus-end
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Text style={{ marginTop: 10, color: 'gray' }}>
Tab press event is prevented
</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
</View>
);
}
const MyTabs = createMaterialTopTabNavigator({
screens: {
Home: HomeScreen,
Settings: SettingsScreen,
},
});
const Navigation = createStaticNavigation(MyTabs);
export default function App() {
return <Navigation />;
}If you have a custom tab bar, make sure to emit this event.
:::note
By default, tabs are rendered lazily. So if you add a listener inside a screen component, it won't receive the event until the screen is focused for the first time. If you need to listen to this event before the screen is focused, you can specify the listener in the screen config instead.
:::
This event is fired when the user presses the tab button for the current screen in the tab bar for an extended period.
Example:
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabLongPress', (e) => {
// Do something
});
return unsubscribe;
}, [navigation]);The tab navigator adds the following methods to the navigation object:
Navigates to an existing screen in the tab navigator. The method accepts following arguments:
name- string - Name of the route to jump to.params- object - Screen params to pass to the destination route.
import * as React from 'react';
import { Text, View } from 'react-native';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
function HomeScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
onPress={
() =>
// codeblock-focus-start
navigation.jumpTo('Profile', { name: 'Michaś' })
// codeblock-focus-end
}
>
Jump to Profile
</Button>
</View>
);
}
function ProfileScreen({ route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
{route.params?.name && (
<Text style={{ marginTop: 10 }}>Name: {route.params.name}</Text>
)}
</View>
);
}
const MyTabs = createMaterialTopTabNavigator({
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
const Navigation = createStaticNavigation(MyTabs);
export default function App() {
return <Navigation />;
}The material top tab navigator exports the following hooks:
This hook returns an object containing an animated value that represents the current position of the tabs. This can be used to animate elements based on the swipe position of the tabs, such as the tab indicator:
import { Animated } from 'react-native';
import { useTabAnimation } from '@react-navigation/material-top-tabs';
function MyView() {
const { position } = useTabAnimation();
return (
<Animated.View
style={{
width: '50%',
height: 2,
backgroundColor: 'tomato',
transform: [{ translateX: position }],
}}
/>
);
}