How to Use Tabs Components in Jetpack Compose (With Usage Examples)
Overview
Tabs in Jetpack Compose organize related content into groups, enabling seamless navigation. PrimaryTabRow
places tabs at the top of a content pane under a top app bar for main destinations, while SecondaryTabRow
separates content within a screen for additional hierarchy.
This guide demonstrates how to implement primary and secondary tabs with navigation and customization options.
When to Use Tabs
Tab Type | When to Use |
---|---|
Primary Tabs | Navigate between main app sections, like Songs, Albums, or Playlists in a music app. |
Secondary Tabs | Organize sub-content within a screen, such as filtering views (e.g., All, Favorites) in a settings section. |
Custom Styled Tabs | Apply branded styling or icons to tabs for enhanced visual hierarchy in complex UIs. |
Creating Tabs Components in Jetpack Compose
Primary Tabs Example
Creates a primary tab row for navigating between main app sections, like Songs, Albums, and Playlists.
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
enum class Destination(val route: String, val label: String) {
SONGS("songs", "Songs"),
ALBUMS("albums", "Albums"),
PLAYLISTS("playlists", "Playlists")
}
@Composable
fun PrimaryTabsExample(modifier: Modifier = Modifier) {
val navController = rememberNavController()
val startDestination = Destination.SONGS
var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
Scaffold(
modifier = modifier,
topBar = {
PrimaryTabRow(selectedTabIndex = selectedDestination) {
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
)
}
)
}
}
}
) { contentPadding ->
NavHost(navController = navController, startDestination = startDestination.route) {
composable(Destination.SONGS.route) { Text("Songs Screen", modifier = Modifier.padding(contentPadding)) }
composable(Destination.ALBUMS.route) { Text("Albums Screen", modifier = Modifier.padding(contentPadding)) }
composable(Destination.PLAYLISTS.route) { Text("Playlists Screen", modifier = Modifier.padding(contentPadding)) }
}
}
}
Result: A horizontal row of three tabs (Songs, Albums, Playlists) at the top, with navigation to corresponding screens.
Scenario: Use for main navigation in apps, such as switching between music app sections like Songs, Albums, or Playlists.
Primary Tabs Properties
Property | Description |
---|---|
selectedTabIndex | Sets the index of the selected tab in PrimaryTabRow . |
selected | Highlights the current tab (e.g., selectedDestination == index ). |
onClick | Handles tab clicks, navigating to a route and updating state. |
text | Displays tab label (e.g., Text(destination.label) ). |
Secondary Tabs Example
Creates a secondary tab row to organize sub-content, such as filtering views in a settings screen.
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
@Composable
fun SecondaryTabsExample(modifier: Modifier = Modifier) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val tabs = listOf("All", "Favorites", "Recent")
SecondaryTabRow(
selectedTabIndex = selectedTabIndex,
modifier = modifier
) {
tabs.forEachIndexed { index, title ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
text = {
Text(
text = title,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
)
}
}
}
Result: A horizontal row of secondary tabs (All, Favorites, Recent) within a content area, updating content based on selection.
Scenario: Use to filter or organize sub-content, such as switching between All, Favorites, or Recent items in a settings or content screen.
Secondary Tabs Properties
Property | Description |
---|---|
selectedTabIndex | Sets the index of the selected tab in SecondaryTabRow . |
selected | Highlights the current tab (e.g., selectedTabIndex == index ). |
onClick | Handles tab clicks, updating the selected tab state. |
text | Displays tab label (e.g., Text(title) ). |
Custom Styled Tabs Example
Creates a primary tab row with custom icons and colors for a branded navigation experience.
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setVar
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MusicNote
import androidx.compose.material.icons.filled.Album
import androidx.compose.material.icons.filled.PlaylistPlay
@Composable
fun CustomStyledTabsExample(modifier: Modifier = Modifier) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val tabs = listOf(
Pair("Songs", Icons.Filled.MusicNote),
Pair("Albums", Icons.Filled.Album),
Pair("Playlists", Icons.Filled.PlaylistPlay)
)
Scaffold(
topBar = {
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
containerColor = Color(0xFF1767d0),
contentColor = Color.White,
modifier = modifier
) {
tabs.forEachIndexed { index, (title, icon) ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
text = {
Text(
text = title,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
icon = {
Icon(
imageVector = icon,
contentDescription = title
)
}
)
}
}
}
) { contentPadding ->
// Screen content
}
}
Result: A branded primary tab row with icons and blue styling, navigating between Songs, Albums, and Playlists.
Scenario: Use for visually enhanced navigation in apps, such as a music app with branded tabs featuring icons for main sections.
Custom Styled Tabs Properties
Property | Description |
---|---|
selectedTabIndex | Sets the index of the selected tab in PrimaryTabRow . |
selected | Highlights the current tab (e.g., selectedTabIndex == index ). |
onClick | Handles tab clicks, updating the selected tab state. |
text | Displays tab label (e.g., Text(title) ). |
icon | Displays an icon in the tab (e.g., Icon(imageVector = icon) ). |
containerColor | Sets the tab row background color (e.g., Color(0xFF1767d0) ). |
contentColor | Sets the tab text/icon color (e.g., Color.White ). |
Key Points for Implementation
- PrimaryTabRow: Places tabs at the top for main navigation.
- SecondaryTabRow: Organizes sub-content within a screen.
- selected: Highlights the active tab based on state.
- onClick: Handles navigation or state updates on tab click.
- text/icon: Defines tab content with labels or icons.
- enabled: Controls tab interactivity (true/false).
- Accessibility: Ensure tabs have clear
contentDescription
for icons and sufficient touch targets (48dp) for screen readers.
Theming Tabs
Customize Tab appearance using MaterialTheme
in Compose or apply specific styles via containerColor
and contentColor
in PrimaryTabRow
or SecondaryTabRow
.
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
@Composable
fun ThemedTabsExample(modifier: Modifier = Modifier) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val tabs = listOf("Songs", "Albums", "Playlists")
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
primary = Color(0xFF1767d0)
)
) {
Scaffold(
topBar = {
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
containerColor = MaterialTheme.colorScheme.primary,
contentColor = Color.White
) {
tabs.forEachIndexed { index, title ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
text = {
Text(
text = title,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
)
}
}
}
) { contentPadding ->
// Screen content
}
}
}
Additional Resources
FAQ
What is a Tabs component?
A component for organizing related content into navigable groups, using primary or secondary tab rows.
Which composables are used for Tabs?
Use Tab
with PrimaryTabRow
for main navigation or SecondaryTabRow
for sub-content.
How to make Tabs accessible?
Ensure tabs have contentDescription
for icons and 48dp touch targets for screen reader compatibility.
Can I customize Tabs appearance?
Yes, via containerColor
, contentColor
, or MaterialTheme.colorScheme
.