Material Design 3 is the next evolution of Material Design. It includes updated theming, components, and Material You personalization features like dynamic color. It's an update to Material Design 2 and is cohesive with the new visual style and system UI on Android 12 and above.
This guide focuses on migrating from the Compose Material (androidx.compose.material) Jetpack library to the Compose Material 3 (androidx.compose.material3) Jetpack library.
Approaches
In general you should not use both M2 and M3 in a single app long-term. This is due to the fact that the two design systems and respective libraries differ significantly in terms of their UX/UI designs and Compose implementations.
Your app may use a design system, such as one created using Figma. In such cases, we also highly recommend that you or your design team migrate it from M2 to M3 before starting the Compose migration. It doesn't make sense to migrate an app to M3 if its UX/UI design is based on M2.
Furthermore, your approach to migration should differ depending on your app's size, complexity, and UX/UI design. Doing so helps you to minimize the impact on your codebase. You should take a phased approach to migration.
When to migrate
You should start the migration as soon as possible. However, it's important to consider whether or not your app is in a realistic position to fully migrate from M2 to M3. There are some blocker scenarios to consider investigating before you start:
Scenario | Recommended approach |
---|---|
No blockers | Begin phased migration |
A component from M2 is not available in M3 yet. See the Components and layouts section below. | Begin phased migration |
You or your design team haven't migrated the app's design system from M2 to M3 | Migrate design system from M2 to M3, then begin phased migration |
Even if you're affected by the above scenarios you should take a phased approach to migration before committing and releasing an app update. In these cases, you would use M2 and M3 side-by-side, and gradually phase out M2 while migrating to M3.
Phased approach
The general steps to a phased migration are as follows:
- Add M3 dependency alongside M2 dependency.
- Add M3 version(s) of your app's theme(s) alongside M2 version(s) of your app's theme(s).
- Migrate individual modules, screens, or composables to M3, depending on the size and complexity of your app (see below sections for details).
- Once fully migrated, remove the M2 version(s) of your app's theme(s).
- Remove M2 dependency.
Dependencies
M3 has a separate package and version to M2:
M2
implementation "androidx.compose.material:material:$m2-version"
M3
implementation "androidx.compose.material3:material3:$m3-version"
See the latest M3 versions on the Compose Material 3 releases page.
Other Material dependencies outside of the main M2 and M3 libraries have not changed. They use a mix of the M2 and M3 packages and versions, but this has no impact on migration. They can be used as-is with M3:
Library | Package and version |
---|---|
Compose Material Icons | androidx.compose.material:material-icons-*:$m2-version |
Compose Material Ripple | androidx.compose.material:material-ripple:$m2-version |
Experimental APIs
Some M3 APIs are considered experimental. In such cases you need to opt in at the function or file level using the ExperimentalMaterial3Api
annotation:
import androidx.compose.material3.ExperimentalMaterial3Api @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppComposable() { // M3 composables }
Theming
In both M2 and M3, the theme composable is named MaterialTheme
but the import packages and parameters differ:
M2
import androidx.compose.material.MaterialTheme MaterialTheme( colors = AppColors, typography = AppTypography, shapes = AppShapes ) { // M2 content }
M3
import androidx.compose.material3.MaterialTheme MaterialTheme( colorScheme = AppColorScheme, typography = AppTypography, shapes = AppShapes ) { // M3 content }
Color

The color system in M3 is significantly different to M2. The number of color parameters has increased, they have different names, and they map differently to M3 components. In Compose, this applies to the M2 Colors
class, the M3 ColorScheme
class, and related functions:
M2
import androidx.compose.material.lightColors import androidx.compose.material.darkColors val AppLightColors = lightColors( // M2 light Color parameters ) val AppDarkColors = darkColors( // M2 dark Color parameters ) val AppColors = if (darkTheme) { AppDarkColors } else { AppLightColors }
M3
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.darkColorScheme val AppLightColorScheme = lightColorScheme( // M3 light Color parameters ) val AppDarkColorScheme = darkColorScheme( // M3 dark Color parameters ) val AppColorScheme = if (darkTheme) { AppDarkColorScheme } else { AppLightColorScheme }
Given the significant differences between the M2 and M3 color systems, there's no reasonable mapping for Color
parameters. Instead, use the Material Theme Builder tool to generate an M3 color scheme. Use the M2 colors as core source colors in the tool, which the tool expands into tonal palettes used by the M3 color scheme. The following mappings are recommended as a starting point:
M2 | Material Theme Builder |
---|---|
primary | Primary |
primaryVariant | Secondary |
secondary | Tertiary |
surface or background | Neutral |

You can copy the color hex code values for light and dark themes from the tool and use them to implement an M3 ColorScheme instance. Alternatively, Material Theme Builder can export Compose code.
isLight
Unlike the M2 Colors
class, the M3 ColorScheme
class doesn't include an isLight
parameter. In general you should try and model whatever needs this information at the theme level. For example:
M2
import androidx.compose.material.lightColors import androidx.compose.material.darkColors import androidx.compose.material.MaterialTheme @Composable private fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) darkColors(…) else lightColors(…) MaterialTheme( colors = colors, content = content ) } @Composable fun AppComposable() { AppTheme { val cardElevation = if (MaterialTheme.colors.isLight) 0.dp else 4.dp … } }
M3
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.MaterialTheme val LocalCardElevation = staticCompositionLocalOf { Dp.Unspecified } @Composable private fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val cardElevation = if (darkTheme) 4.dp else 0.dp CompositionLocalProvider(LocalCardElevation provides cardElevation) { val colorScheme = if (darkTheme) darkColorScheme(…) else lightColorScheme(…) MaterialTheme( colorScheme = colorScheme, content = content ) } } @Composable fun AppComposable() { AppTheme { val cardElevation = LocalCardElevation.current … } }
See the Custom design systems in Compose guide for more information.
Dynamic color
A new feature in M3 is dynamic color. Instead of using custom colors, an M3 ColorScheme
can make use of device wallpaper colors on Android 12 and above, using the following functions:
Typography

The typography system in M3 is different to M2. The number of typography parameters is roughly the same, but they have different names and they map differently to M3 components. In Compose, this applies to the M2 Typography
class and the M3 Typography
class:
M2
import androidx.compose.material.Typography val AppTypography = Typography( // M2 TextStyle parameters )
M3
import androidx.compose.material3.Typography val AppTypography = Typography( // M3 TextStyle parameters )
The following TextStyle
parameter mappings are recommended as a starting point:
M2 | M3 |
---|---|
h1 | displayLarge |
h2 | displayMedium |
h3 | displaySmall |
N/A | headlineLarge |
h4 | headlineMedium |
h5 | headlineSmall |
h6 | titleLarge |
subtitle1 | titleMedium |
subtitle2 | titleSmall |
body1 | bodyLarge |
body2 | bodyMedium |
caption | bodySmall |
button | labelLarge |
N/A | labelMedium |
overline | labelSmall |
Shape

The shape system in M3 is different to M2. The number of shape parameters has increased, they're named differently and they map differently to M3 components. In Compose, this applies to the M2 Shapes
class and the M3 Shapes
class:
M2
import androidx.compose.material.Shapes val AppShapes = Shapes( // M2 Shape parameters )
M3
import androidx.compose.material3.Shapes val AppShapes = Shapes( // M3 Shape parameters )
The following Shape
parameter mappings are recommended as a starting point:
M2 | M3 |
---|---|
N/A | extraSmall |
small | small |
medium | medium |
large | large |
N/A | extraLarge |
Components and layouts
Most components and layouts from M2 are available in M3. There are, however, some missing as well as new ones that didn't exist in M2. Furthermore, some M3 components have more variations than their equivalents in M2. In general the M3 API surfaces attempt to be as similar as possible to their closest equivalents in M2.
Given the updated color, typography and shape systems, M3 components tend to map differently to the new theming values. It's a good idea to check out the tokens directory in the Compose Material 3 source code as a source of truth for these mappings.
While some components require special considerations, the following function mappings are recommended as a starting point:
Missing APIs:
M2 | M3 |
---|---|
androidx.compose.material.swipeable | Not available yet |
Replaced APIs:
M2 | M3 |
---|---|
androidx.compose.material.BackdropScaffold | No M3 equivalent, migrate to Scaffold or BottomSheetScaffold instead |
androidx.compose.material.BottomDrawer | No M3 equivalent, migrate to ModalBottomSheet instead |
Renamed APIs:
All other APIs:
See the latest M3 components and layouts on the Compose Material 3 API reference overview, and keep an eye out on the releases page for new and updated APIs.
Scaffold, snackbars and navigation drawer

Scaffold in M3 is different to M2. In both M2 and M3, the main layout composable is named Scaffold
but the import packages and parameters differ:
M2
import androidx.compose.material.Scaffold Scaffold( // M2 scaffold parameters )
M3
import androidx.compose.material3.Scaffold Scaffold( // M3 scaffold parameters )
The M2 Scaffold
contains a backgroundColor
parameter is now named to containerColor
in the M3 Scaffold
:
M2
import androidx.compose.material.Scaffold Scaffold( backgroundColor = …, content = { … } )
M3
import androidx.compose.material3.Scaffold Scaffold( containerColor = …, content = { … } )
The M2 ScaffoldState
class no longer exists in M3 as it contains a drawerState
parameter which is no longer needed. To show snackbars with the M3 Scaffold
, use SnackbarHostState
instead:
M2
import androidx.compose.material.Scaffold import androidx.compose.material.rememberScaffoldState val scaffoldState = rememberScaffoldState() val scope = rememberCoroutineScope() Scaffold( scaffoldState = scaffoldState, content = { … scope.launch { scaffoldState.snackbarHostState.showSnackbar(…) } } )
M3
import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, content = { … scope.launch { snackbarHostState.showSnackbar(…) } } )
All of the drawer*
parameters from the M2 Scaffold
have been removed from the M3 Scaffold
. These include parameters such as drawerShape
and drawerContent
. To show a drawer with the M3 Scaffold
, use a navigation drawer composable, such as ModalNavigationDrawer
, instead:
M2
import androidx.compose.material.DrawerValue import import androidx.compose.material.Scaffold import androidx.compose.material.rememberDrawerState import androidx.compose.material.rememberScaffoldState val scaffoldState = rememberScaffoldState( drawerState = rememberDrawerState(DrawerValue.Closed) ) val scope = rememberCoroutineScope() Scaffold( scaffoldState = scaffoldState, drawerContent = { … }, drawerGesturesEnabled = …, drawerShape = …, drawerElevation = …, drawerBackgroundColor = …, drawerContentColor = …, drawerScrimColor = …, content = { … scope.launch { scaffoldState.drawerState.open() } } )
M3
import androidx.compose.material3.DrawerValue import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Scaffold import androidx.compose.material3.rememberDrawerState val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope() ModalNavigationDrawer( drawerState = drawerState, drawerContent = { ModalDrawerSheet( drawerShape = …, drawerTonalElevation = …, drawerContainerColor = …, drawerContentColor = …, content = { … } ) }, gesturesEnabled = …, scrimColor = …, content = { Scaffold( content = { … scope.launch { drawerState.open() } } ) } )
Top app bar

Top app bars in M3 are different to those in M2. In both M2 and M3, the main top app bar composable is named TopAppBar
but the import packages and parameters differ:
M2
import androidx.compose.material.TopAppBar TopAppBar(…)
M3
import androidx.compose.material3.TopAppBar TopAppBar(…)
Consider using the M3 CenterAlignedTopAppBar
if you were previously centering content within the M2 TopAppBar
. It's good to be aware of the MediumTopAppBar
and LargeTopAppBar
as well.
M3 top app bars contain a new scrollBehavior
parameter to provide different functionality on scroll through the TopAppBarScrollBehavior
class, such as changing elevation. This works in conjunction with scrolling content via Modifer.nestedScroll
. This was possible in the M2 TopAppBar
by manually changing the elevation
parameter:
M2
import androidx.compose.material.AppBarDefaults import androidx.compose.material.Scaffold import androidx.compose.material.TopAppBar val state = rememberLazyListState() val isAtTop by remember { derivedStateOf { state.firstVisibleItemIndex == 0 && state.firstVisibleItemScrollOffset == 0 } } Scaffold( topBar = { TopAppBar( elevation = if (isAtTop) { 0.dp } else { AppBarDefaults.TopAppBarElevation }, … ) }, content = { LazyColumn(state = state) { … } } )
M3
import androidx.compose.material3.Scaffold import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopAppBar( scrollBehavior = scrollBehavior, … ) }, content = { LazyColumn { … } } )
Bottom navigation / Navigation bar

Bottom navigation in M2 has been renamed to navigation bar in M3. In M2 there are the BottomNavigation
and BottomNavigationItem
composables, while in M3 there are the NavigationBar
and NavigationBarItem
composables:
M2
import androidx.compose.material.BottomNavigation import androidx.compose.material.BottomNavigationItem BottomNavigation { BottomNavigationItem(…) BottomNavigationItem(…) BottomNavigationItem(…) }
M3
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem NavigationBar { NavigationBarItem(…) NavigationBarItem(…) NavigationBarItem(…) }
Buttons, icon buttons and FABs

Buttons, icon buttons and floating action buttons (FABs) in M3 are different to those in M2. M3 includes all of the M2 button composables:
M2
import androidx.compose.material.Button import androidx.compose.material.ExtendedFloatingActionButton import androidx.compose.material.FloatingActionButton import androidx.compose.material.IconButton import androidx.compose.material.IconToggleButton import androidx.compose.material.OutlinedButton import androidx.compose.material.TextButton // M2 buttons Button(…) OutlinedButton(…) TextButton(…) // M2 icon buttons IconButton(…) IconToggleButton(…) // M2 FABs FloatingActionButton(…) ExtendedFloatingActionButton(…)
M3
import androidx.compose.material3.Button import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.IconButton import androidx.compose.material3.IconToggleButton import androidx.compose.material3.OutlinedButton import androidx.compose.material3.TextButton // M3 buttons Button(…) OutlinedButton(…) TextButton(…) // M3 icon buttons IconButton(…) IconToggleButton(…) // M3 FABs FloatingActionButton(…) ExtendedFloatingActionButton(…)
M3 also includes new button variations. Check them out on the Compose Material 3 API reference overview.
Switch

Switch in M3 is different to M2. In both M2 and M3, the switch composable is named Switch
but the import packages differ:
M2
import androidx.compose.material.Switch Switch(…)
M3
import androidx.compose.material3.Switch Switch(…)
Surfaces and elevation

The surface and elevation systems in M3 are different to M2. There are two types of elevation in M3:
- Shadow elevation (casts a shadow, same as M2)
- Tonal elevation (overlays a color, new to M3)
In Compose this applies to the M2 Surface
function and the M3 Surface
function:
M2
import androidx.compose.material.Surface Surface( elevation = … ) { … }
M3
import androidx.compose.material3.Surface Surface( shadowElevation = …, tonalElevation = … ) { … }
You can use the elevation
Dp
values in M2 for both shadowElevation
and/or tonalElevation
in M3, depending on the UX/UI design preference. Surface
is the backing composable behind most components, so component composables might also expose elevation parameters you must migrate in the same way.
Tonal elevation in M3 replaces the concept of elevation overlays in M2 dark themes . As a result, ElevationOverlay
and LocalElevationOverlay
don't exist in M3, and LocalAbsoluteElevation
in M2 has changed to LocalAbsoluteTonalElevation
in M3.
Emphasis and content alpha

Emphasis in M3 is significantly different to M2. In M2, emphasis involved using on colors with certain alpha values to differentiate content like text and icons. In M3, there are now a couple different approaches:
- Using on colors alongside their variant on colors from the expanded M3 color system.
- Using different font weights for text.
As a result, ContentAlpha
and LocalContentAlpha
don't exist in M3 and need to be replaced.
The following mappings are recommended as a starting point:
M2 | M3 |
---|---|
onSurface with ContentAlpha.high | onSurface in general, FontWeight.Medium - FontWeight.Black for text |
onSurface with ContentAlpha.medium | onSurfaceVariant in general, FontWeight.Thin - FontWeight.Normal for text |
onSurface with ContentAlpha.disabled | onSurface.copy(alpha = 0.38f) |
Here's an example of icon emphasis in M2 vs. M3:
M2
import androidx.compose.material.ContentAlpha import androidx.compose.material.LocalContentAlpha // High emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { Icon(…) } // Medium emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Icon(…) } // Disabled emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon(…) }
M3
import androidx.compose.material3.LocalContentColor // High emphasis CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) { Icon(…) } // Medium emphasis CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) { Icon(…) } // Disabled emphasis CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) { Icon(…) }
Here are examples of text emphasis in M2 and M3:
M2
import androidx.compose.material.ContentAlpha import androidx.compose.material.LocalContentAlpha // High emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { Text(…) } // Medium emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text(…) } // Disabled emphasis CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text(…) }
M3
import androidx.compose.material3.LocalContentColor // High emphasis Text( …, fontWeight = FontWeight.Bold ) // Medium emphasis Text( …, fontWeight = FontWeight.Normal ) // Disabled emphasis CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) { Text( …, fontWeight = FontWeight.Normal ) }
Backgrounds and containers
Backgrounds in M2 are named containers in M3. In general, you can replace background*
parameters in M2 with container*
in M3, using the same values. For example:
M2
Badge( backgroundColor = MaterialTheme.colors.primary ) { … }
M3
Badge( containerColor = MaterialTheme.colorScheme.primary ) { … }
Useful links
To learn more about migrating from M2 to M3 in Compose, consult the following additional resources.
Docs
Sample apps
- Reply M3 sample app
- Jetchat sample app M2 to M3 migration
- Jetnews sample app M2 to M3 migration
- Now in Android M3 hero app :core-designsystem module
Videos
API reference and source code
Recommended for you
- Note: link text is displayed when JavaScript is off
- Material Design 2 in Compose
- Material Design 3 in Compose
- Custom design systems in Compose