Tabs allow you to organize groups of related content. There are two types of tabs:
- Primary tabs: Placed at the top of the content pane under a top app bar. They display the main content destinations, and should be used when just one set of tabs are needed.
- Secondary tabs: Used within a content area to further separate related content and establish hierarchy. They are necessary when a screen requires more than one level of tabs.

This page shows how to display primary tabs in your app with related screens and basic navigation.
API surface
Use the Tab
, PrimaryTabRow
, and SecondaryTabRow
composables
to implement tabs. The Tab
composable represents an individual tab within the
row, and is typically used inside of a PrimaryTabRow
(for primary indicator
tabs) or SecondaryTabRow
(for secondary indicator tabs).
Tab
includes the following key parameters:
selected
: Determines whether the current tab is visually highlighted.onClick()
: A required lambda function that defines the action to be performed when the user clicks on the tab. This is where you typically handle navigation events, update the selected tab state, or load corresponding content.text
: Displays text within the tab. Optional.icon
: Displays an icon within the tab. Optional.enabled
: Controls whether the tab is enabled and can be interacted with. If set to false, the tab is displayed in a disabled state and won't respond to clicks.
Example: Tab-based navigation
The following snippet implements a top navigation bar with tabs to navigate between different screens in an app:
@Composable fun NavigationTabExample(modifier: Modifier = Modifier) { val navController = rememberNavController() val startDestination = Destination.SONGS var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) } Scaffold(modifier = modifier) { contentPadding -> PrimaryTabRow(selectedTabIndex = selectedDestination, modifier = Modifier.padding(contentPadding)) { Destination.entries.forEachIndexed { index, destination -> Tab( selected = selectedDestination == index, onClick = { navController.navigate(route = destination.route) selectedDestination = index }, text = { Text( text = destination.label, maxLines = 2, overflow = TextOverflow.Ellipsis ) } ) } } AppNavHost(navController, startDestination) } }
Key points
PrimaryTabRow
displays a horizontal row of tabs, with each tab corresponding to aDestination
.val navController = rememberNavController()
creates and remembers an instance ofNavHostController
, which manages the navigation within aNavHost
.var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
manages the state of the currently selected tab.startDestination.ordinal
gets the numerical index (position) of theDestination.SONGS
enum entry.
- When a tab is clicked,
navController.navigate(route = destination.route)
is called to navigate to the corresponding screen. - The
onClick
lambda of theTab
updates theselectedDestination
state to visually highlight the clicked tab. - It calls the
AppNavHost
composable, passing thenavController
andstartDestination
, to display the actual content of the selected screen.
Result
The following image shows the result of the previous snippet:
.png)