Migration strategy

Stay organized with collections Save and categorize content based on your preferences.

If you have an app with a View-based UI, you may not want to rewrite its entire UI all at once. This page will help you add new Compose elements into your existing UI.

Jetpack Compose was designed with View interoperability right from the start. This functionality means you can migrate your existing View-based app to Compose while still being able to build new features. To migrate to Compose, we recommend an incremental migration where Compose and Views co-exist in your codebase until your app is fully in Compose.

The stages of a View-based app’s migration to Compose
Figure 1. The stages of a View-based app’s migration to Compose

To migrate your app to Compose, follow these steps:

  1. Build new features with Compose.
  2. As you’re building features, identify reusable elements and start to create a library of common UI components.
  3. Replace existing features one screen at a time.

Build new features with Compose

Using Compose to build new features is the best way to drive your adoption of Compose. With this strategy, you can add features and take advantage of the benefits of Compose while still catering to your company’s business needs.

New screens

A new screen written in Compose
Figure 2. A new screen written in Compose

When you use Compose to build new features in your existing app, you're still working under the constraints of your app’s architecture. If you are using Fragments and the Navigation component, and the feature you’re building encompasses an entire screen, then you would have to create a new Fragment but its contents would be in Compose.

To use Compose in a Fragment, return a ComposeView in the onCreateView() lifecycle method of your Fragment. ComposeView has a setContent() method where you can provide a composable function.

class NewFeatureFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                NewFeatureScreen()
            }
        }
    }
}

See ComposeView in Fragments to learn more.

Existing screens

An existing screen with mixed Views and Compose
Figure 3. An existing screen with mixed Views and Compose

If your new feature is going to be part of an existing screen, you can add ComposeView to the UI hierarchy, just like any other View.

For example, say you want to add a child view to a LinearLayout. You can do so in XML as follows:

<LinearLayout ...>

  <Button ... />

  <TextView ... />

  <androidx.compose.ui.platform.ComposeView
    android:id="@+id/compose_view" ... />

</LinearLayout>

Once the view has been inflated, you can later reference the ComposeView in the hierarchy and call setContent().

To learn more about ComposeView, check out Interoperability APIs.

Build a library of common UI components

As you’re building features with Compose, you’ll quickly realize that you end up building a library of components. Creating a library of common UI components allows you to have a single source of truth for these components in your app and promote reusability. Features you build can then depend on this library. This technique is especially useful if you are building a custom design system in Compose.

Depending on your app’s size, this library could be a separate package, module, or library module. For more information on organizing modules in your app, check out the Guide to Android app modularization.

Replace existing features with Compose

In addition to using Compose to build new features, you’ll want to gradually migrate existing features in your app to take advantage of Compose.

Having your app be Compose-only can accelerate your development and also reduce the APK size and build times of your app. See Developer Ergonomics in Compose to learn more.

Simple screens

The first places to look when migrating existing features to Compose are simple screens. Simple screens can be a welcome screen, a confirmation screen, or a setting screen wherein the data displayed in the UI is relatively static.

Take the following XML file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

 <TextView
   android:id="@+id/title_text"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/title"
     android:textAppearance="?attr/textAppearanceHeadline2" />

 <TextView
     android:id="@+id/subtitle_text"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/subtitle"
     android:textAppearance="?attr/textAppearanceHeadline6" />

 <TextView
     android:id="@+id/body_text"
     android:layout_width="wrap_content"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:text="@string/body"
     android:textAppearance="?attr/textAppearanceBody1" />

 <Button
     android:id="@+id/confirm_button"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="@string/confirm"/>

</LinearLayout>

The XML file can be rewritten in Compose in a few lines:

@Composable
fun SimpleScreen() {
    Column(Modifier.fillMaxSize()) {
        Text(
            text = stringResource(R.string.title),
            style = MaterialTheme.typography.headlineMedium
        )
        Text(
            text = stringResource(R.string.subtitle),
            style = MaterialTheme.typography.headlineSmall
        )
        Text(
            text = stringResource(R.string.body),
            style = MaterialTheme.typography.bodyMedium
        )
        Spacer(modifier = Modifier.weight(1f))
        Button(onClick = { /* Handle click */ }, Modifier.fillMaxWidth()) {
            Text(text = stringResource(R.string.confirm))
        }
    }
}

Mixed view and Compose screens

A screen that already contains a bit of Compose code is another good candidate for migrating entirely to Compose. Depending on the complexity of the screen, you can either migrate it entirely to Compose, or do it piece-by-piece. If the screen started with Compose in a subtree of the UI hierarchy, you would continue migrating UI elements until the entire screen is in Compose. This approach is also called the bottom-up approach.

Bottom-up approach of migrating a mixed Views and Compose UI to Compose
Figure 4. Bottom-up approach of migrating a mixed Views and Compose UI to Compose

Removing Fragments and Navigation component

Once all of the UI in your app is in Compose, there’s little benefit to using Fragments in your app. Once you get to that point, you can remove Fragments entirely, and instead replace them with screen-level composables driven by Navigation Compose.

See Navigating with Compose to learn more.

Next steps

If you want to dive right into adding Compose to your app, check out Adding Jetpack Compose to your app. You can also check out the following resources: