Compose Unstyled 2.0 is out! Check the official announcement blog ->

Tabs with horizontal and vertical lists, icons, and animated indicators.

Use tabs for switching between related views without leaving the current screen.

View on GitHub
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.BookOpen
import com.composables.icons.lucide.Code
import com.composables.icons.lucide.Eye
import com.composables.icons.lucide.Lucide
import com.composables.ui.components.Icon
import com.composables.ui.components.Tab
import com.composables.ui.components.TabList
import com.composables.ui.components.TabOrientation
import com.composables.ui.components.TabPanel
import com.composables.ui.components.Text
import com.composables.ui.components.Tabs

@Composable
fun TabsExample() {
    val tabs = listOf("Preview", "Code", "Docs")
    var selected by remember { mutableStateOf(tabs.first()) }
    Tabs(
        selectedTab = selected,
        onSelectedTabChange = { selected = it },
        orderedTabs = tabs,
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            TabList {
                tabs.forEach { tab ->
                    Tab(key = tab, text = { Text(tab) })
                }
            }
            tabs.forEach { tab ->
                TabPanel(tab) {
                    Text("$tab content")
                }
            }
        }
    }
}

Installation

implementation("com.composables:ui:0.1.0")

Examples

Weighted

View on GitHub
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Tab
import com.composables.ui.components.TabList
import com.composables.ui.components.TabPanel
import com.composables.ui.components.Tabs
import com.composables.ui.components.Text

@Composable
fun WeightedTabsExample() {
    val tabs = listOf("Preview", "Code", "Docs")
    var selected by remember { mutableStateOf(tabs.first()) }
    Tabs(
        selectedTab = selected,
        onSelectedTabChange = { selected = it },
        orderedTabs = tabs,
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            TabList(modifier = Modifier.fillMaxWidth()) {
                tabs.forEach { tab ->
                    Tab(
                        key = tab,
                        modifier = Modifier.weight(1f),
                        text = {
                            Text(tab)
                        },
                    )
                }
            }
            tabs.forEach { tab ->
                TabPanel(tab) {
                    Text("$tab content")
                }
            }
        }
    }
}

With icons

View on GitHub
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.BookOpen
import com.composables.icons.lucide.Code
import com.composables.icons.lucide.Eye
import com.composables.icons.lucide.Lucide
import com.composables.ui.components.Icon
import com.composables.ui.components.Tab
import com.composables.ui.components.TabList
import com.composables.ui.components.TabPanel
import com.composables.ui.components.Tabs
import com.composables.ui.components.Text

@Composable
fun TabsWithIconsExample() {
    val tabs = listOf(
        TabsExampleTab("Preview", Lucide.Eye),
        TabsExampleTab("Code", Lucide.Code),
        TabsExampleTab("Docs", Lucide.BookOpen),
    )
    var selected by remember { mutableStateOf(tabs.first()) }
    Tabs(
        selectedTab = selected,
        onSelectedTabChange = { selected = it },
        orderedTabs = tabs,
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            TabList {
                tabs.forEach { tab ->
                    Tab(
                        key = tab,
                        icon = {
                            Icon(
                                imageVector = tab.icon,
                                contentDescription = null,
                                modifier = Modifier.size(20.dp),
                            )
                        },
                        text = {
                            Text(tab.label)
                        },
                    )
                }
            }
            tabs.forEach { tab ->
                TabPanel(tab) {
                    Text("${tab.label} content")
                }
            }
        }
    }
}

private data class TabsExampleTab(
    val label: String,
    val icon: ImageVector,
)

Vertical

View on GitHub
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Tab
import com.composables.ui.components.TabList
import com.composables.ui.components.TabOrientation
import com.composables.ui.components.TabPanel
import com.composables.ui.components.Tabs
import com.composables.ui.components.Text

@Composable
fun VerticalTabsExample() {
    val tabs = listOf("Preview", "Code", "Docs")
    var selected by remember { mutableStateOf(tabs.first()) }
    Tabs(
        selectedTab = selected,
        onSelectedTabChange = { selected = it },
        orderedTabs = tabs,
        modifier = Modifier.fillMaxWidth(),
    ) {
        Row(
            modifier = Modifier
                .widthIn(max = 420.dp)
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            TabList(orientation = TabOrientation.Vertical) {
                tabs.forEach { tab ->
                    Tab(key = tab, text = { Text(tab) })
                }
            }
            tabs.forEach { tab ->
                TabPanel(
                    key = tab,
                    modifier = Modifier.weight(1f),
                ) {
                    Text("$tab content")
                }
            }
        }
    }
}

Disabled

View on GitHub
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Tab
import com.composables.ui.components.TabList
import com.composables.ui.components.TabPanel
import com.composables.ui.components.Tabs
import com.composables.ui.components.Text

@Composable
fun DisabledTabsExample() {
    val tabs = listOf("Preview", "Code", "Docs")
    var selected by remember { mutableStateOf(tabs.first()) }
    Tabs(
        selectedTab = selected,
        onSelectedTabChange = { selected = it },
        orderedTabs = tabs,
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            TabList {
                tabs.forEach { tab ->
                    Tab(
                        key = tab,
                        enabled = tab != "Code",
                        text = { Text(tab) },
                    )
                }
            }
            tabs.forEach { tab ->
                TabPanel(tab) {
                    Text("$tab content")
                }
            }
        }
    }
}

API Reference

Tabs

Tabs that coordinate tab triggers and panels.

@Composable
fun <T> Tabs(
    selectedTab: T,
    onSelectedTabChange: (T) -> Unit,
    orderedTabs: List<T>,
    modifier: Modifier = Modifier,
    content: @Composable TabsScope<T>.() -> Unit,
)
Parameter Type Description
selectedTab T Currently selected tab key.
onSelectedTabChange (T) -> Unit Called when the selected tab changes.
orderedTabs List<T> Ordered list of tab keys used for indicator placement and navigation.
modifier Modifier Modifier applied to the component.
content @Composable TabsScope<T>.() -> Unit Composable content displayed by the component.

Tab

An individual tab inside a tab list.

@Composable
fun <T> Tab(
    key: T,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    activateOnFocus: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    indication: Indication? = Theme[indications][defaultIndication],
    icon: (@Composable () -> Unit)? = null,
    text: @Composable () -> Unit,
)
Parameter Type Description
key T Key represented by this tab or tab panel.
modifier Modifier Modifier applied to the component.
enabled Boolean Whether the tab can be interacted with.
activateOnFocus Boolean Whether focusing the tab should immediately select it.
interactionSource MutableInteractionSource Interaction source used for focus, hover, and press state.
indication Indication? Indication used when the tab is pressed or focused.
icon (@Composable () -> Unit)? Optional icon content shown before the tab text.
text @Composable () -> Unit Text content shown inside the tab.

TabList

A container that arranges tab triggers and draws the active indicator.

@Composable
fun <T> TabsScope<T>.TabList(
    modifier: Modifier = Modifier,
    orientation: TabOrientation = TabOrientation.Horizontal,
    dividerColor: Color = Theme[colors][borderColor],
    content: @Composable StackScope.() -> Unit,
)
Parameter Type Description
modifier Modifier Modifier applied to the component.
orientation TabOrientation Orientation used to lay out the tab list.
dividerColor Color Color used for the indicator divider line.
content @Composable StackScope.() -> Unit Composable content displayed by the component.

TabPanel

Content associated with a specific tab key.

@Composable
fun <T> TabsScope<T>.TabPanel(
    key: T,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
)
Parameter Type Description
key T Key represented by this tab or tab panel.
modifier Modifier Modifier applied to the component.
content @Composable () -> Unit Composable content displayed by the component.

TabOrientation

Orientation options for arranging tab lists.

@JvmInline
value class TabOrientation internal constructor(internal val orientation: Orientation)
Value Description
Horizontal Arranges tabs in a horizontal row.
Vertical Arranges tabs in a vertical column.