For more than a decade, Android screens were built from XML layouts and Views updated by hand from Kotlin or Java. Jetpack Compose replaces that with UI written as Kotlin functions that describe what the screen should look like for a given state. Google recommends Compose for new Android UI, and most new libraries and samples assume it. For teams with an existing app, the real question is how to move without stopping feature work. Here is what changes and how to migrate safely.
What Changes With Compose
| Topic | XML Views | Jetpack Compose |
|---|---|---|
| Defining UI | XML layout files inflated into View objects | Kotlin functions annotated with @Composable |
| Updating UI | Find the view and call setText, setVisibility, and similar methods | Change the state, and Compose redraws the affected parts |
| Lists | RecyclerView with an adapter and ViewHolder | LazyColumn and LazyRow with a few lines of code |
| Theming | Styles and themes in XML resources | MaterialTheme with colours, typography, and shapes in Kotlin |
| Previews | Layout editor, limited for dynamic content | @Preview functions with sample data, several states side by side |
The biggest shift is mental. In the View system you change the UI step by step. In Compose you describe the UI for the current state, and a whole class of bugs disappears, such as a loading spinner that stays visible because one code path forgot to hide it.
Should You Migrate?
- New screens are the easiest win. Building them in Compose costs nothing extra and the team learns on real work.
- Screens that change often, with complex lists or many states, benefit most from a rewrite.
- Stable screens that nobody touches can stay in XML for a long time. Interop is well supported.
- Custom Views with heavy drawing code are the last to move, and can be wrapped in AndroidView inside Compose in the meantime.
Migrate Screen by Screen
- 1Add the Compose dependencies through the Compose BOM, which keeps library versions compatible, and enable the Compose compiler plugin that ships with Kotlin 2.0 and later.
- 2Create a MaterialTheme that matches your current colours and fonts, so Compose screens look the same as XML ones.
- 3Build reusable components first, such as buttons, text fields, and list items, as a small design system.
- 4Write new screens in Compose. Host them with ComposeView inside an existing Fragment or with setContent in an Activity.
- 5Move a ViewModel-backed screen by keeping the ViewModel and replacing only the layout, then collecting its StateFlow with collectAsStateWithLifecycle.
- 6Once most screens are migrated, consider Navigation Compose to replace Fragment-based navigation.
Handle State the Compose Way
Keep screen state in a ViewModel that exposes one UI state object through StateFlow, and pass plain data and event callbacks down to composables. This is called state hoisting. A composable that receives its data as parameters is easy to preview with sample data and easy to test. Use remember for small UI details such as whether a menu is expanded, and rememberSaveable for values that must survive rotation, like text being typed.
Performance Habits
- Judge performance in release builds only. Debug builds of Compose are much slower and give a false picture.
- Add Baseline Profiles so key code paths are compiled ahead of time, which improves startup and scrolling.
- Give items in LazyColumn a stable key so Compose can reuse them correctly when the list changes.
- Avoid expensive work directly in composables. Compute it in the ViewModel or wrap it in remember with the right keys.
- Use the Layout Inspector to see recomposition counts and find composables that redraw too often.
Testing
Compose UI tests use semantics, the same information accessibility services read. createComposeRule lets you set content, find nodes by text or test tag, perform clicks, and assert what is shown. Because composables take state as parameters, many tests can render one screen in each state without starting the whole app. Screenshot tests based on previews are a good addition for catching visual regressions.
The safest migration is the one that never stops feature work. Build new things in Compose and move old screens when you would touch them anyway.
Our Android Kotlin training teaches Compose from the start, including interop with existing XML screens, so teams with older apps can apply it to their own codebase in the first week.
Key takeaways
- Compose describes UI as a function of state, which removes many bugs caused by updating Views by hand.
- Start with new screens and frequently changed screens, and leave stable ones in XML until there is a reason to move them.
- Use the Compose BOM, a shared MaterialTheme, and reusable components before migrating screens.
- Hoist state into a ViewModel and pass data and callbacks down to composables.
- Measure performance in release builds, add Baseline Profiles, and give list items stable keys.


