Navigation Drawer in Jetpack Compose – Complete Guide for Beginners
A Navigation Drawer is one of the most commonly used navigation components in Android applications. It provides a sliding side panel that contains the primary navigation options of your app, allowing users to switch between different screens quickly and efficiently.
Jetpack Compose, Google's modern UI toolkit for Android, makes it easy to build beautiful and responsive Navigation Drawers using Material 3 components. In this tutorial, you'll learn what a Navigation Drawer is, its advantages, types, implementation, best practices, and frequently asked questions.
What is a Navigation Drawer?
A Navigation Drawer is a side menu that slides in from the left side of the screen (or the right side in RTL languages). It contains navigation links, user profile information, settings, and other important sections of an application.
It is commonly used in:
- E-commerce applications
- Social media apps
- Banking applications
- Learning platforms
- Dashboard apps
- News applications
Why Use a Navigation Drawer?
A Navigation Drawer offers several benefits for applications with multiple top-level destinations.
- Easy navigation between screens.
- Supports many menu items.
- Provides a clean and organized user interface.
- Works well on phones and tablets.
- Follows Material Design guidelines.
- Improves overall user experience.
Material 3 Components Used
| Component | Purpose |
|---|---|
ModalNavigationDrawer |
Main container that displays the navigation drawer. |
ModalDrawerSheet |
Displays the drawer content. |
NavigationDrawerItem |
Represents an individual menu item. |
DrawerState |
Controls the drawer's open and closed states. |
rememberDrawerState() |
Stores the drawer state across recompositions. |
Types of Navigation Drawer
1. Modal Navigation Drawer
The Modal Navigation Drawer slides over the current screen and temporarily hides the content behind it.
Best for: Smartphones
2. Permanent Navigation Drawer
A Permanent Navigation Drawer remains visible on large screens like tablets and desktop devices.
Best for: Tablets, Foldables, ChromeOS
Typical Navigation Drawer Structure
- Profile Image
- User Name
- Email Address
- Home
- Profile
- Messages
- Favorites
- Settings
- Help & Support
- Logout
How Navigation Drawer Works
- User taps the menu icon.
- The drawer slides in from the side.
- User selects a destination.
- The selected screen opens.
- The drawer automatically closes.
Navigation Drawer vs Bottom Navigation
| Navigation Drawer | Bottom Navigation |
|---|---|
| Slides from the side | Fixed at the bottom |
| Supports many destinations | Best for 3–5 destinations |
| Ideal for medium and large apps | Ideal for small apps |
| Can include profile and settings | Mainly for navigation only |
| Usually hidden until opened | Always visible |
Real-World Examples
- Google Drive
- Gmail
- YouTube
- Google Classroom
- Microsoft Teams
- Amazon Shopping
Best Practices
- Keep only important navigation destinations.
- Use meaningful Material icons.
- Highlight the selected item.
- Close the drawer after selecting a destination.
- Organize menu items using dividers.
- Add profile information only when necessary.
- Use descriptive labels.
- Maintain proper spacing and padding.
- Support accessibility and screen readers.
- Use a Permanent Drawer on larger screens.
Common Mistakes to Avoid
- Adding too many menu items.
- Using unclear labels.
- Forgetting to highlight the selected item.
- Not closing the drawer after navigation.
- Using inconsistent icons.
- Ignoring accessibility guidelines.
Advantages
- Professional app navigation.
- Clean interface.
- Supports multiple screens.
- Easy to customize.
- Material Design compliant.
- Responsive across devices.
Disadvantages
- Requires an extra tap to access.
- Not ideal for apps with only a few screens.
- Can become cluttered if overloaded with menu items.
Lets Build a Simple Navigation Drawer using Jetpack Compose
Dependency wee need:
Dependencies
implementation("androidx.navigation:navigation-compose:2.9.8")
implementation("androidx.compose.material:material-icons-extended")
implementation("io.coil-kt:coil-compose:2.7.0")
Drawer Menu Model
data class DrawerItem(
val title: String,
val icon: ImageVector,
val badge: Int? = null
)
Menu List
val drawerItems = listOf(
DrawerItem("Home", Icons.Default.Home),
DrawerItem("Profile", Icons.Default.Person),
DrawerItem("Messages", Icons.AutoMirrored.Filled.Message,5),
DrawerItem("Favorites", Icons.Default.Favorite),
DrawerItem("History", Icons.Default.History),
DrawerItem("Settings", Icons.Default.Settings),
DrawerItem("Help & Support", Icons.AutoMirrored.Filled.Help),
DrawerItem("Logout", Icons.AutoMirrored.Filled.ExitToApp)
)
Main Screen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavigationDrawerDemo() {
val drawerState =
rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
DrawerContent(drawerItems)
}
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text("Navigation Drawer")
},
navigationIcon = {
IconButton(
onClick = {
scope.launch {
drawerState.open()
}
}
) {
Icon(
Icons.Default.Menu,
null
)
}
}
)
},
floatingActionButton = {
FloatingActionButton(
onClick = {}
) {
Icon(Icons.Default.Add,null)
}
}
) { padding ->
Box(
Modifier
.fillMaxSize()
.padding(padding),
contentAlignment = Alignment.Center
) {
Text(
"Home Screen"
)
}
}
}
}
Drawer Content
@Composable
fun DrawerContent(
items: List
) {
ModalDrawerSheet(
modifier = Modifier.width(320.dp)
) {
DrawerHeader()
Spacer(Modifier.height(8.dp))
items.forEach {
NavigationDrawerItem(
label = {
Text(it.title)
},
icon = {
Box {
Icon(
it.icon,
null
)
if(it.badge!=null){
Badge(
modifier = Modifier
.align(
Alignment.TopEnd
)
){
Text("${it.badge}")
}
}
}
},
selected = it.title=="Home",
onClick = {}
)
}
}
}
Drawer Header
@Composable
fun DrawerHeader() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(240.dp)
.background(
Brush.verticalGradient(
listOf(
Color(0xFF6A1BFF),
Color(0xFF4A00E0)
)
)
)
) {
Column(
modifier = Modifier
.padding(20.dp)
.align(
Alignment.BottomStart
)
) {
Image(
painter = painterResource(
R.drawable.profile
),
contentDescription = null,
modifier = Modifier
.size(90.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(Modifier.height(16.dp))
Text(
"Sandeep Kumar",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Text(
"sandeepkumar@gmail.com",
color = Color.White.copy(.9f)
)
}
}
}Outpur:
Another Example:
implementation("androidx.navigation:navigation-compose-android:2.9.0")
Before diving in, make sure you have:
- Android Studio Meerkat or above
- Kotlin 1.9+
- Jetpack Compose set up in your project
π§± What We'll Build
- A Navigation Drawer Layout using ModalNavigationDrawer
- A Top App Bar to open the drawer
- A List of Navigation Items
- A modern, beautiful UI with icons and drawer header
MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
BasicOfJetpackComposeIn30DaysTheme {
NavDrawer()
}
}
}
}
NavDrawer
@Composable
fun NavDrawer() {
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet(
modifier = Modifier
.fillMaxHeight()
.clip(RoundedCornerShape(topEnd = 24.dp, bottomEnd = 24.dp))
.background(MaterialTheme.colorScheme.surface)
) {
// Drawer Header
Row(
modifier = Modifier
.padding(20.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "My Drawer",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
}
IconButton(onClick = {
scope.launch { drawerState.close() }
}) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Close Drawer",
tint = MaterialTheme.colorScheme.onSurface
)
}
}
HorizontalDivider()
// Drawer Items
val drawerItems = listOf("Home", "Profile", "Settings", "Logout")
drawerItems.forEachIndexed { index, item ->
NavigationDrawerItem(
label = { Text(text = item) },
selected = false,
onClick = { },
modifier = Modifier.padding(horizontal = 12.dp),
icon = {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = null
)
}
)
}
}
}
) {
// Open Drawer Button
IconButton(
onClick = {
scope.launch {
if (drawerState.isClosed) drawerState.open()
else drawerState.close()
}
},
modifier = Modifier
.padding(18.dp)
.background(MaterialTheme.colorScheme.primaryContainer, CircleShape)
.size(48.dp)
) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = "Open Drawer",
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
// Main Content
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
listOf(
MaterialTheme.colorScheme.primaryContainer,
MaterialTheme.colorScheme.background
)
)
),
contentAlignment = Alignment.Center
) {
Card(
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(8.dp),
modifier = Modifier.padding(32.dp)
) {
Text(
text = "Welcome to the Modern UI!",
modifier = Modifier.padding(24.dp),
style = MaterialTheme.typography.headlineSmall
)
}
}
}
}OUTPUT:
// Close drawer on back press
if (drawerState.isOpen) {
BackHandler {
scope.launch {
drawerState.close()
}
}
}Final Code:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavDrawer() {
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
// Close drawer on back press
if (drawerState.isOpen) {
BackHandler {
scope.launch { drawerState.close() }
}
}
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet(
modifier = Modifier
.fillMaxHeight()
.clip(RoundedCornerShape(topEnd = 24.dp, bottomEnd = 24.dp))
.background(MaterialTheme.colorScheme.surface)
) {
// Drawer Header
Row(
modifier = Modifier
.padding(20.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "My Drawer",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
}
IconButton(onClick = {
scope.launch { drawerState.close() }
}) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Close Drawer",
tint = MaterialTheme.colorScheme.onSurface
)
}
}
HorizontalDivider()
// Drawer Items
val drawerItems = listOf("Home", "Profile", "Settings", "Logout")
drawerItems.forEach { item ->
NavigationDrawerItem(
label = { Text(text = item) },
selected = false,
onClick = { /* Handle item click */ },
modifier = Modifier.padding(horizontal = 12.dp),
icon = {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = null
)
}
)
}
}
}
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Modern UI") },
navigationIcon = {
IconButton(
onClick = {
scope.launch {
if (drawerState.isClosed) drawerState.open()
else drawerState.close()
}
}
) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = "Toggle Drawer"
)
}
}
)
}
) { innerPadding ->
Box(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
.background(
Brush.verticalGradient(
listOf(
MaterialTheme.colorScheme.primaryContainer,
MaterialTheme.colorScheme.background
)
)
),
contentAlignment = Alignment.Center
) {
Card(
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(8.dp),
modifier = Modifier.padding(32.dp)
) {
Text(
text = "Welcome to the Modern UI!",
modifier = Modifier.padding(24.dp),
style = MaterialTheme.typography.headlineSmall
)
}
}
}
}
}Final Result:
π Done! What You’ve Learned
- Custom drawer layout with top bar
- Seamless navigation between screens
- Icon support and drawer header
- Modular, scalable Compose architecture
Navigation between screens using Navigation Component for Compose
π§© Step 1: Add Dependencies
implementation("androidx.navigation:navigation-compose-android:2.9.0")π§ Step 2: Create a Navigation Drawer Item Data Class
data class DrawerItem(
val route: String,
val icon: ImageVector,
val title: String
)π¨ Step 3: Define Navigation Destinations
sealed class Screen(val route: String) {
object Home : Screen("home")
object Profile : Screen("profile")
object Settings : Screen("settings")
}π️ Step 4: Create Screens
@Composable
fun HomeScreen() {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("π Home Screen", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun ProfileScreen() {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("π€ Profile Screen", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun SettingsScreen() {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("⚙️ Settings Screen", style = MaterialTheme.typography.headlineMedium)
}
}Step 5: Create Navigation Host
@Composable
fun AppNavigation(navController: NavHostController) {
NavHost(navController, startDestination = Screen.Home.route) {
composable(Screen.Home.route) { HomeScreen() }
composable(Screen.Profile.route) { ProfileScreen() }
composable(Screen.Settings.route) { SettingsScreen() }
}
}Step 6: Build the Drawer UI
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen() {
val navController = rememberNavController()
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
val items = listOf(
DrawerItem("Home", Icons.Default.Home, Screen.Home.route),
DrawerItem("Profile", Icons.Default.Person, Screen.Profile.route),
DrawerItem("Settings", Icons.Default.Settings, Screen.Settings.route),
)
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
Column(modifier = Modifier.fillMaxSize()) {
DrawerHeader()
items.forEach { item ->
NavigationDrawerItem(
label = { Text(item.title) },
icon = { Icon(item.icon, contentDescription = item.title) },
selected = false,
onClick = {
scope.launch {
drawerState.close()
}
navController.navigate(item.route)
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
}
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Jetpack Compose Drawer") },
navigationIcon = {
IconButton(onClick = {
scope.launch { drawerState.open() }
}) {
Icon(Icons.Default.Menu, contentDescription = "Menu")
}
}
)
}
) {
Box(modifier = Modifier.padding(it)) {
AppNavigation(navController)
}
}
}
}πΌ️ Step 7: Add a Custom Drawer Header
@Composable
fun DrawerHeader() {
Box(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.primaryContainer)
.padding(24.dp),
contentAlignment = Alignment.CenterStart
) {
Column {
Text("Hello, Developer!", style = MaterialTheme.typography.headlineSmall)
Text("explore the app", style = MaterialTheme.typography.bodyMedium)
}
}
}
π± Final Setup in MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
BasicOfJetpackComposeIn30DaysTheme {
MainScreen()
}
}
}
}Final Output:
Frequently Asked Questions (FAQ) – Navigation Drawer in Jetpack Compose
1. What is a Navigation Drawer in Jetpack Compose?
A Navigation Drawer is a side panel that slides in from the left (or right in RTL layouts) and displays navigation options, user information, settings, and other app destinations. It helps users navigate between different sections of an application quickly.
2. Why should I use a Navigation Drawer?
A Navigation Drawer is ideal for applications with multiple top-level destinations that cannot fit into a Bottom Navigation Bar.
Common use cases:
- E-commerce apps
- News applications
- Learning platforms
- Banking apps
- Dashboard applications
3. What are the types of Navigation Drawers available in Jetpack Compose?
- Modal Navigation Drawer – Slides over the current screen.
- Permanent Navigation Drawer – Always visible, mainly used on tablets and desktops.
4. Which composable is used to create a Navigation Drawer?
Jetpack Compose Material 3 provides the ModalNavigationDrawer composable.
ModalNavigationDrawer(
drawerContent = { ... }
) {
// Screen Content
}
5. What is ModalDrawerSheet?
ModalDrawerSheet is the container that holds the drawer content such as menu items, profile information, and settings.
ModalDrawerSheet {
Text("Home")
Text("Profile")
}
6. How do I open the Navigation Drawer programmatically?
Use DrawerState with a coroutine.
scope.launch {
drawerState.open()
}
7. How do I close the Navigation Drawer?
scope.launch {
drawerState.close()
}
8. What is rememberDrawerState()?
It creates and remembers the drawer state across recompositions.
val drawerState = rememberDrawerState(
initialValue = DrawerValue.Closed
)
9. What drawer states are available?
DrawerValue.OpenDrawerValue.Closed
10. Why is CoroutineScope required?
Opening and closing the drawer are suspend functions, so they must be called inside a coroutine.
val scope = rememberCoroutineScope()
scope.launch {
drawerState.open()
}
11. How do I add icons to drawer items?
NavigationDrawerItem(
label = { Text("Home") },
icon = {
Icon(Icons.Default.Home, null)
},
selected = false,
onClick = { }
)
12. Can I display user profile information inside the drawer?
Yes. You can place an image, name, email address, or any custom composables at the top of the drawer.
Column {
Image(...)
Text("John Doe")
Text("john@email.com")
}
13. Does Navigation Drawer work with Navigation Compose?
Yes. It integrates perfectly with NavHost and NavController.
navController.navigate("profile")
14. How do I highlight the selected drawer item?
NavigationDrawerItem(
selected = currentRoute == "home",
onClick = { },
label = { Text("Home") }
)
15. Can I customize the drawer background color?
Yes.
ModalDrawerSheet(
drawerContainerColor = Color.White
)
16. Can I change the width of the drawer?
Yes.
ModalDrawerSheet(
modifier = Modifier.width(300.dp)
)
17. How do I disable swipe gestures?
ModalNavigationDrawer(
gesturesEnabled = false,
drawerState = drawerState,
drawerContent = { }
) {
// Content
}
18. Can I place the Navigation Drawer on the right side?
Yes. Jetpack Compose automatically supports Right-to-Left (RTL) layouts. When the app runs in an RTL language such as Arabic, the drawer appears on the right side.
19. What is the difference between Navigation Drawer and Bottom Navigation?
| Navigation Drawer | Bottom Navigation |
|---|---|
| Slides from the side | Fixed at the bottom |
| Supports many destinations | Best for 3–5 destinations |
| Ideal for large apps | Ideal for small apps |
| Can include profile and settings | Focuses on primary navigation |
| Hidden until opened | Always visible |
20. Is Navigation Drawer available in Material 3?
Yes. Material 3 provides:
- ModalNavigationDrawer
- ModalDrawerSheet
- PermanentNavigationDrawer
- PermanentDrawerSheet
- NavigationDrawerItem
21. Can I add a custom header?
Yes. You can place any composables before the drawer items.
Column {
Text("Coding Bihar")
HorizontalDivider()
}
22. How do I separate drawer sections?
Use HorizontalDivider().
HorizontalDivider()
23. Can the drawer content be scrollable?
Yes.
Column(
modifier = Modifier.verticalScroll(
rememberScrollState()
)
) {
// Drawer Items
}
24. Should every app use a Navigation Drawer?
No. A Navigation Drawer is best for apps with multiple sections or many top-level destinations. If your app has only a few primary screens, a Bottom Navigation Bar or Navigation Rail is usually a better choice.
25. What are the best practices for using a Navigation Drawer?
- Keep only important destinations in the drawer.
- Use meaningful icons with clear labels.
- Highlight the currently selected item.
- Close the drawer automatically after navigation.
- Group related items using dividers.
- Add a user profile section when appropriate.
- Follow Material Design spacing and accessibility guidelines.
- Use Permanent Navigation Drawer on large-screen devices.
- Avoid adding too many menu items.
- Test the drawer with keyboard navigation and screen readers.
Conclusion
Navigation Drawer is one of the most useful navigation components in Jetpack Compose for medium and large applications. By using Material 3 components such as ModalNavigationDrawer and NavigationDrawerItem, you can create modern, responsive, and user-friendly navigation experiences that work seamlessly across phones, tablets, and foldable devices.





