At Google I/O 2026, the Android team made an announcement that sounded technical but carries real business weight. All future UI work from Google will happen in Jetpack Compose, and the older Views system, which Android apps have relied on since the first phones shipped in 2008, is moving into maintenance mode. Maintenance mode means the old system keeps working and keeps getting bug fixes. It stops getting new features.
Google's Material Design team made the same move and now focuses its component work on Compose. So the buttons, sliders, sheets and animations that make an app feel current on Android will show up in Compose first, and in many cases only there. Compose is now Google's main Android UI toolkit, and everything else is legacy.
If you run a startup with an Android app, that changes how you plan the next year of product work. If you write code, it settles what to learn next.
The easy part of Compose fits in a paragraph. The harder part, how it behaves with missing data, conflicting updates, tight frame deadlines and millions of users, is where most of this piece goes.
How Android screens were built before
For most of Android's history, building a screen meant working in two places at once. You drew the layout in an XML file, a text file full of tags that says "put a title here, a picture there, a button under it." Then you wrote separate code, in Java or later Kotlin, that looked up each of those pieces by name and changed them by hand when something happened.
Say a shopping app shows "3 items in cart." When the user adds a fourth item, the code has to find the badge, change its text, and maybe make it visible if it was hidden. Each change is a separate instruction. Forget one and the screen shows something that is no longer true.
Think of a whiteboard in an office where someone has to walk over, erase and rewrite each number whenever the underlying spreadsheet changes. With two numbers, that's fine. With forty numbers updated by five people, the whiteboard drifts out of sync with the spreadsheet, and nobody notices until a customer does.
That drift caused a large share of old Android UI bugs. The layout file said one thing, the code assumed another, and the actual data in the app was a third thing. Scrolling lists made it worse, because they needed "adapters," a block of setup code whose only job was to recycle rows as they scrolled off screen and fill them with new data.
The Compose idea, in plain words
Jetpack Compose flips the approach. Instead of writing instructions that change the screen step by step, you write a description of what the screen should look like for a given set of data. When the data changes, Compose works out which parts of the screen are affected and redraws only those.
Back to the whiteboard: Compose replaces it with a TV screen wired directly to the spreadsheet. Nobody walks over to erase anything. The display always matches the data, because it is produced from the data.
Here is a tiny example so the idea feels concrete:
@Composable
fun CartBadge(itemCount: Int) {
if (itemCount > 0) {
Text("$itemCount items in cart")
}
}
Even without programming experience, you can probably read this: if the count is above zero, show the count. There's no step where the code finds the badge and hides it when the cart empties. When the count drops to zero, Compose runs this function again, sees there's nothing to show, and removes the text.
Three terms come up constantly, so it helps to pin them down:
• A composable is a function, a named block of code, that describes a piece of UI. CartBadge above is one. Screens are built by nesting small composables inside bigger ones.
• State is any piece of data that can change and should be reflected on screen: the cart count, whether a menu is open, the text in a search box.
• Recomposition is Compose running a composable again because the state it reads has changed. It's the redraw step, and it's selective. Parts of the screen that didn't read the changed state get skipped.
Everything happens in Kotlin, the programming language Google has recommended for Android since 2019. There's no separate layout language to learn and no jumping between files to understand one screen. That single-language setup is a big reason teams find Kotlin UI development with Compose easier to read and review.
The numbers behind the shift
Google has published adoption figures for Jetpack Compose at regular points since the 1.0 release in July 2021. They track the share of the 1,000 most popular apps on the Google Play Store that ship Compose code in production.
That's a move from one in six top apps to more than two in three in under four years, with Google Drive and the MAX streaming app among the names Google cited in 2025.
A few other figures add context:
• Android ran on 67.61% of mobile devices worldwide in August 2026, according to StatCounter Global Stats, with iOS at 32.36%. In most markets, Android is the bigger audience.
• When Google rebuilt its own Play Store app with Compose, the team reported in 2022 that UI took much less code to write, in some places up to 50% less.
• StatCounter's July 2026 data puts Android 16 on 25.71% of Android phones, while Android 11 and Android 12 together still account for about 18%. That spread comes up again later, because a UI toolkit has to behave the same way across years of devices.
That last point connects to how Compose is delivered. It ships as a library inside each app, while the older Android UI toolkit was built into the phone's operating system. Because of that, Compose runs the same code on an Android 12 phone and an Android 16 phone, and Google can release updates every few months without waiting for Samsung, Xiaomi or anyone else to update their devices.
Put the adoption curve next to the Compose-first announcement, and most teams are now deciding how fast to adopt it and which screens to move first.
How Compose compares with the other options
Teams choosing a way to build Android screens in 2026 usually weigh four options. Two come from Google: the old Views system and Compose. The other two are cross-platform frameworks that target Android and iOS from one codebase: Flutter, also from Google and written in a language called Dart, and React Native, from Meta, written in JavaScript or TypeScript.
The word "native" gets used loosely, so it's worth being exact. Here, native Android apps means apps built with Google's own tools, running Kotlin directly on the device, with access to every Android feature the day it ships. Compose apps are native in that sense. Flutter and React Native apps can feel close to native, but they sit on an extra layer between your code and the operating system. That layer is where delays in new feature support and odd platform bugs tend to show up.
Neither cross-platform option is a bad choice. If a startup has three engineers and needs iOS and Android on the same day, one shared codebase might decide whether the company ships at all. The math changes when Android is the main platform, or when the product depends on Android-specific features like home screen widgets, Wear OS watches, foldable layouts or deep system integrations.
The part tutorials skip: how Compose behaves when things go wrong
Most introductions stop at "describe the UI and Compose updates it." That's accurate for a demo. Production apps deal with messy data, slow networks, impatient users and cheap phones.
When the data isn't there yet
Every screen that loads something from the internet spends some time with nothing to show. Compose pushes you to handle that gap on purpose, because the screen is described from the data. If the data can be "not loaded yet," the description has to say what to show in that case. Good teams model each screen's state as a short, fixed list of possibilities, something like loading, loaded with content, loaded but empty, and failed. The screen's composable picks one branch for each.
That sounds obvious, but the gaps hide between those cases:
• Partial data. A product page might get its title and price from one server call and its reviews from another. If the whole screen has one "loading" flag, the user stares at a spinner even though most of the page is ready. Splitting state per section lets the price appear while reviews are still on the way.
• Stale data. An app that caches data for offline use can show yesterday's price while it fetches today's. The state needs a way to say "here is content, and it might be old," so the screen can show a small "updating" label instead of pretending the price is current.
• Empty versus missing. An empty order history and a failed request both mean "no rows to show," but they need different screens. Merge them and you tell a paying customer they've never ordered anything.
Pro tip: Before building a screen, write down every state it can be in, including the awkward ones like "offline with a cached copy." If the list runs past six or seven entries, the screen is probably trying to do too much and should be split.
When two sources tell the screen different things
Conflicting signals are harder than missing ones. Take a "like" button. The user taps it, and the app fills in the heart immediately, before the server confirms. This is called an optimistic update, and it makes the app feel fast. Then the server replies with an error, or with a like count that doesn't match the local one. Which should the screen believe?
Compose doesn't answer that question for you, but its structure makes the answer easier to enforce. The pattern Google recommends is a single source of truth: one object, usually a ViewModel (a class that holds a screen's data and survives events like the phone rotating), owns the real state. The UI never keeps its own separate copy. When the server disagrees, the ViewModel decides what wins, updates its state once, and every composable reading that state redraws to match.
Text fields are the classic trap. If the text a user types lives in a ViewModel and gets updated asynchronously, meaning with a small delay because it passes through other code first, the typed characters and the stored value can briefly disagree. Users see the cursor jump or letters vanish. The older Compose text field APIs were easy to misuse this way. The newer state-based text field, built around an object called TextFieldState, keeps typing inside the field itself and lets the rest of the app observe it, which removes that race.
A third kind of conflict comes from Android itself. Android can close an app in the background to free memory and quietly restore it when the user comes back. Compose has two ways to hold on to a value: remember, which keeps it while the screen is displayed, and rememberSaveable, which also survives rotation and that background shutdown. Picking the wrong one produces a bug that almost never appears in testing, because testers rarely leave an app for an hour on a low-memory phone. The user returns to a half-filled form, and it's blank.
Decisions made inside a 16-millisecond window
Phones redraw the screen many times per second. At 60 frames per second, the app has about 16.7 milliseconds to produce each frame. On a 120 Hz display, now common on mid-range and flagship phones, that drops to about 8.3 milliseconds. Miss the window and the frame gets dropped, which users feel as stutter, or jank.
Compose builds each frame in three phases. Composition decides what to show. Layout measures each element and decides where it goes. Drawing paints the pixels. If some piece of state is read only during layout or drawing, Compose can skip composition entirely when that state changes.
This matters most for values that change every frame: scroll position, animations, drag gestures. A header that fades as the user scrolls is a good example. If the composable reads the scroll position directly, every tiny scroll movement reruns composition for the header, dozens of times per second. If it reads the scroll position inside a drawing step instead, only drawing reruns. Compose offers versions of its modifiers, such as graphicsLayer, that take a lambda (a small function evaluated later) for exactly this reason.
A related tool is derivedStateOf. Suppose a "back to top" button should appear once the user scrolls past the first item. The scroll position changes constantly, but the answer to "past the first item?" changes rarely. derivedStateOf lets the button recompose only when that yes-or-no answer flips.
Pro tip: When a screen stutters, check whether a value that changes every frame is being read in a composable's body. Moving that read into a layout or drawing lambda is often the entire fix.
Edge cases that catch experienced teams
Some problems only show up in specific situations:
• Lists without keys. In a scrolling list built with LazyColumn, each row should carry a stable key, such as an order ID. Without keys, Compose tracks rows by position. Delete the third row and every row below it looks changed, so they all recompose, and per-row state like an expanded section can jump to the wrong item.
• Inputs Compose can't trust. Compose skips redrawing a composable when its inputs haven't changed, but it has to know whether an input can change without notice. Classes from other modules or libraries used to be treated as untrustworthy, so composables that used them redrew every time. Since the Kotlin 2.0.20 compiler, a setting called strong skipping mode is on by default and relaxes this rule, though older projects often still carry workarounds written for the earlier behavior.
• Side effects that restart. Code meant to run once, such as logging a screen view or starting a countdown, goes inside helpers like LaunchedEffect along with a key value. Pass the wrong key and the effect restarts whenever some unrelated value changes, firing analytics events twice or resetting the timer.
• Accessibility and large fonts. Compose builds the information screen readers use from "semantics" attached to each element. Custom-drawn buttons without semantics are invisible to screen readers. Layouts with fixed heights clip text when a user sets the system font size to 200%, which Android allows for people with low vision.
• Old views inside Compose. Some components still exist only in the old system, including certain map, ad and video SDKs. Compose can host them through a wrapper called AndroidView, but they don't benefit from Compose's skipping, and dropping them into a fast-scrolling list is a known source of jank.
• Foldables and resizable windows. On foldables, tablets and desktop-style windows, the available space changes while the app runs. Compose treats that as another state change, which is one of its real strengths, but only if layouts respond to available width instead of assuming a phone.
What happens under pressure and at scale
On a single screen, LazyColumn and LazyRow compose only the rows currently visible plus a small buffer, so a list of 10,000 orders costs roughly what a list of 30 does. Trouble starts when each row is expensive: large images decoded on the main thread, lists nested inside lists, or very different row types mixed without hints. Giving rows a contentType, a label saying "this is a header" or "this is a product card," helps Compose reuse similar rows efficiently.
The first launch is a less obvious pressure point. Android speeds up app code over time as the device learns which parts get used most and compiles them ahead of time. On first launch that learning hasn't happened, so the first scroll can feel slow. Baseline Profiles fix this by shipping a list of the most important code paths with the app, so the phone can prepare them at install time. Google's Android documentation says Baseline Profiles improve code execution speed by about 30% from the first launch.
Then there's a false alarm that misleads a lot of teams: debug builds. The version of an app developers run while building it includes extra tooling and skips the optimizations applied to release builds. Compose in a debug build can feel sluggish in ways the shipped app never does. Measure on a release build, on a mid-range phone, with Android's Macrobenchmark library.
On the team side, Compose changes how large apps get organized. Because UI is plain Kotlin functions, companies build internal design systems as libraries of composables: their own button, card, text styles and spacing rules. When a designer changes the brand color, one edit to the theme updates every screen that uses it. In the old system, a style change often meant hunting through hundreds of XML files. The trade-off is build time. Compose relies on a compiler plugin, and very large modules can compile slowly, so teams at scale split code into many smaller modules.
Moving an existing app without stopping the business
If you already have an app built with Views, nobody has to rewrite it in one go. Compose was designed to live alongside Views. A Compose section can sit inside an old-style screen through a component called ComposeView, and an old view can sit inside Compose through AndroidView. That two-way bridge lets teams migrate a piece at a time.
A path that works for many teams:
• Start with new features only. Every new screen gets built in Compose while old screens stay as they are. The team builds skill without touching working code.
• Build the design system next. Rebuilding the company's buttons, text styles and colors as composables early stops each developer from inventing a private version.
• Move the screens that change often. A settings page nobody has touched in two years can wait. The checkout flow that product edits every sprint is where Compose saves the most time.
• Leave risky, rarely touched screens for last, or leave them alone entirely if they work.
• Measure before and after. Track startup time, dropped frames and crash rate for each migrated screen, so the business can see whether the change helped.
App size behaves differently during the process. While both systems are in the app, the download is bigger, because it carries two UI libraries. Google's write-up on migrating its Tivi sample app reported the APK, the app's install file, shrinking by about 46% once the migration finished and the old View-based libraries were removed. The size cost is mostly a transition cost.
For founders planning budget, the main cost is developer time spent learning. A developer comfortable with Kotlin usually gets productive with Compose within a few weeks and picks up its performance habits over a few months. The hiring market has moved too. Many newer Android developers learned Kotlin UI development through Compose first and find XML layouts unfamiliar, so a Views-only codebase gets harder to staff every year.
Pro tip: When interviewing Android developers, ask how they would handle a screen whose data arrives from two sources at different times. The answer tells you more about their Compose experience than any list of APIs they can recite.
When Compose isn't the right call
Compose is the default now, but there are honest reasons to pick something else or to wait:
• You need iOS and Android at once with a very small team, and the design is identical on both. Flutter or React Native may get you to launch faster. Compose Multiplatform is worth a look here too, since JetBrains declared its iOS support stable in 2025.
• You're building a game. Engines like Unity or Godot handle rendering, physics and sound in ways neither Compose nor Views were built for.
• Your app is stable, earns money and has no UI work planned. The Views system still works and still gets fixes. Migrating screens nobody touches spends money without changing anything users see.
• You depend on a third-party SDK that only ships old-style views and behaves badly when wrapped. Check this before committing to a timeline.
Most teams building native Android apps in 2026 won't hit any of these.
Beyond the phone
Compose now reaches well past phone screens. According to the Android Developers Blog, Google offers Compose for TV, Compose for Wear OS watches, Glance for home screen widgets, and a Compose library called Glimmer for display glasses. A team that knows Compose can build for all of them with the same Kotlin UI development skills and much of the same code structure.
JetBrains, the company behind Kotlin, extends the same approach to iOS, desktop and web through Compose Multiplatform. iOS support reached stable status in 2025, while web support is still maturing. That gives Android-first companies a middle path: keep native Android apps as the core product and share UI code with iOS where it makes sense, instead of rebuilding everything in a separate cross-platform framework.
Google's 2026 releases also added adaptive layout tools, including FlexBox, Grid and MediaQuery APIs, for apps that need to look right on phones, foldables, tablets and desktop windows. With Views frozen for new features, tools like these will exist only on the Compose side.
Where this leaves you
Five years after its 1.0 release, Jetpack Compose has gone from an experiment to the way Google expects Android screens to be built. More than two in three of the top 1,000 Play Store apps already use it, and the old system has stopped growing.
The basics take an afternoon to grasp. The skills that separate a smooth app from a janky one take longer: modeling state honestly, keeping fast-changing values out of composition, and profiling release builds on ordinary phones. Teams with an existing app can get there gradually, screen by screen, starting where the product changes most. And anyone comparing frameworks now has a clearer picture than in years past. If Android matters most to your product, Google's Android UI toolkit is Compose, and new platform features will land there first.


