Skip to content

feat: agp v9 adoption - #1006

Open
hurali97 wants to merge 2 commits into
react-native-community:mainfrom
hurali97:proposal/agp9-adoption
Open

feat: agp v9 adoption#1006
hurali97 wants to merge 2 commits into
react-native-community:mainfrom
hurali97:proposal/agp9-adoption

Conversation

@hurali97

@hurali97 hurali97 commented Jun 9, 2026

Copy link
Copy Markdown

Proposal: Android Gradle Plugin v9 Adoption

View the rendered RFC

@hurali97 hurali97 changed the title feat: add proposal for agp v9 adoption feat: agp v9 adoption Jun 9, 2026
Updated AGP v9 adoption proposal with changes for React Native core and community libraries, including Gradle upgrades and migration patterns.
Comment thread proposals/0000-android-agp-v9-adoption.md
@hurali97
hurali97 marked this pull request as ready for review June 10, 2026 14:54
@lukmccall

Copy link
Copy Markdown

The RFC looks great, thanks for putting it together! I've already started working on AGP9 support on the Expo side and opened a few PRs. Most things are working well, but I've hit a few unexpected issues that'll need more work. For instance, we have some custom Apollo client configuration in the dev-launcher that stopped working. I'll share more once I figure out how to fix it.

@lukmccall

Copy link
Copy Markdown

One random thought: what do you guys think about creating a kotlin-android-compat Gradle plugin that would apply kotlin-android conditionally based on the AGP version and whether the built-in Kotlin is enabled? It would make migration a bit easier for library authors, as most packages could then be compatible with both AGP 8 and 9.

@hurali97

Copy link
Copy Markdown
Author

One random thought: what do you guys think about creating a kotlin-android-compat Gradle plugin that would apply kotlin-android conditionally based on the AGP version and whether the built-in Kotlin is enabled? It would make migration a bit easier for library authors, as most packages could then be compatible with both AGP 8 and 9.

This sounds like a nice idea and could help with one part of the migration, but after thinking about it more, I’m not sure it would add enough value on its own.

Even with a kotlin-android-compat plugin, library authors would still need to handle a few manual migration steps:

  • Remove kotlin-android / org.jetbrains.kotlin.android from their build.gradle for AGP 9+, since built-in Kotlin replaces that plugin there.
  • Remove or migrate deprecated/removed APIs such as android.kotlinOptions and usages of .srcDirs where they are no longer valid.
  • Avoid configuring Kotlin-only sources through java.srcDirs.
  • Add AGP-version checks for the remaining compatibility differences anyway.

The custom plugin could dynamically apply kotlin-android for AGP < 9, which is useful. But it cannot really solve the case where the consuming project has already applied kotlin-android, because we can apply plugins programmatically, but we can’t safely “unapply” a plugin that was already applied by the build script.

So in practice, the plugin would cover only one part of the migration: conditionally applying kotlin-android for AGP 8 and below. The removal of kotlin-android for AGP 9+, plus the other DSL/source-set migrations, would still require manual changes from library authors. Because of that, I think the value might be relatively limited.

@lukmccall

lukmccall commented Jun 12, 2026

Copy link
Copy Markdown

Yes, I agree it won't solve every problem - realistically, this will still require some manual changes from library maintainers. But other changes, such as using kotlin.srcDirs instead of java.srcDirs, should work with both AGP versions. I'm only suggesting we add it to make supporting both possible in the majority of cases with minimal effort from the maintainer's perspective. The issue is that if you're using the plugins block, you can't apply plugins conditionally without falling back to the old apply plugin syntax - such a plugin would solve that.

@hurali97

hurali97 commented Jun 12, 2026

Copy link
Copy Markdown
Author

The issue is that if you're using the plugins block, you can't apply plugins conditionally without falling back to the old apply plugin syntax

True that. So If I catch it correctly, the library maintainers will need to replace the kotlin-android plugin with compat Gradle plugin?

For some libraries, this might be the only change that they need, for eg, react-native-pager-view. However, for majority the authors will need other manual adjustments.

I am also wondering whether to introduce a separate compat plugin OR we can add this as a fallback mechanism to RNGP? That way the library authors may not need to implement another plugin.

Nicola suggested that we can also think of separating library implementations from RNGP in a separate Gradle plugin, however that is optional for now and we will start that separately than AGP 9. I am mentioning it here because if we are able to add this compat mechanism to RNGP, we can later extract it out.

But other changes, such as using kotlin.srcDirs instead of java.srcDirs, should work with both AGP versions

Yes it should work, however there are a few catches. The kotlin-only sources can not be added to java.srcDirs, they should be added to kotlin.srcDirs, otherwise with built-in kotlin on AGP 9 there is a build crash. The .srcDirs is deprecated in favor of .directories with AGP v9, so in the future versions this maybe removed.

@cortinico cortinico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great RFC @hurali97 (sorry for the late review).

I've left some comments. I know that @lukmccall @Kudo have some updates on the Expo front to share also


In Phase 2, we will submit compatibility pull requests to actively used and maintained libraries from the matrix above while preserving backward compatibility. Broader ecosystem coverage will require community participation.

As part of Phase 2, the Expo team will evaluate required changes on their side, including ecosystem impact.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there an update on this front? @lukmccall @Kudo ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On our side, we're ready. All Expo code should work with AGP 9 and be compatible with build-in Kotlin. There might be some leftovers that I missed. I'll retest everything whenever RN officially starts supporting AGP 9.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On our side, we're ready

How is expo going to handle the removal of the opt-out?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the module's perspective, the Expo Gradle plugin handles all 3 cases: AGP 8, AGP 9 with built-in Kotlin opted out, and AGP 9 with built-in Kotlin support.

For users who use CNG, we'll apply the changes in the app template automatically. For the rest, they'll have to adjust their apps themselves (we can't do anything more here).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lukmccall My question was more related to the 3p libraries that have imports of kotlin-android which will break the build (once we remove the opt-out)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly. Can we expand the RFC with the kotlin-android-compat plugin and how we're planning to distribute it? cc @hurali97

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can expand the RFC, but we’ll need details on how Expo plans to publish and handle the usage of this plugin. cc: @lukmccall

Additionally, I think this plugin should remain optional, especially for non Expo projects. If library maintainers have already added backward compatibility for AGP v9 or simply prefer to manage it manually, they should be free to do so.

We should also highlight this in the upcoming React Native blog post section on AGP v9 adoption: developers can either use the compatibility plugin or handle backward compatibility themselves.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can expand the RFC, but we’ll need details on how Expo plans to publish and handle the usage of this plugin

Hmm, I think we should publish that plugin to Maven under the React Foundation - similar to how React Native is published. Publishing it under Expo seems weird, as in our case we don't need it at all. However, if you think that would be easier, we can handle it on our side.

Additionally, I think this plugin should remain optional, especially for non Expo projects. If library maintainers have already added backward compatibility for AGP v9 or simply prefer to manage it manually, they should be free to do so.

I agree that the plugin should remain optional

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think we should publish that plugin to Maven under the React Foundation - similar to how React Native is published. Publishing it under Expo seems weird, as in our case we don't need it at all. However, if you think that would be easier, we can handle it on our side.

If we're planning to land a separate library plugin anyway, we could bundle the kotlin-android-compat logic directly into it rather than publishing a separate plugin. Since tomorrow's draft PR for separate library plugin will likely need a few iterations. Publishing the standalone kotlin-android-compat plugin first might be the faster route. However, if we're flexible with the timeline, encapsulating that logic inside library plugin and have only this applied by the consumers. cc: @cortinico

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're planning to land a separate library plugin anyway, we could bundle the kotlin-android-compat logic directly into it rather than publishing a separate plugin

Yes ideally that's the preferred outcome.


As described above, AGP v9 adoption is rolled out in three phases. Phase 1 is complete and should ship with React Native v0.87.x. Newly created apps do not need changes because template opt-outs are enabled by default. Existing apps upgrading to v0.87.x should apply a few manual updates described in the next section. At this stage, we expect no breakages with actively maintained community libraries.

In Phase 2, we will submit compatibility pull requests to actively used and maintained libraries from the matrix above while preserving backward compatibility. Broader ecosystem coverage will require community participation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while preserving backward compatibility

This is the key of the whole proposal. I'm wondering how can we update libraries to don't apply kotlin-android while still retaining backward compat.

In the past we talked about a com.facebook.react.library Gradle Plugin that is versioned outside the react/react-native repo. Is this something we're still interested in driving?

If so let's add it in this RFC. cc @lukmccall @Kudo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can apply kotlin-android conditionally using our own Gradle plugin - it would look like this: https://github.com/expo/expo/blob/7d1359bd667168ca9ae64afc17058e558df23163/packages/expo-modules-core/expo-module-gradle-plugin/src/main/kotlin/expo/modules/plugin/ProjectConfiguration.kt#L23-L33. Not sure if we should combine this RFC with the RN turbomodules Gradle plugin. Maybe it would be better to publish kotlin-android-compat first to only address the AGP migration, as proposed above: #1006 (comment)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On our end, as part of a POC, we've separated the Gradle setup into RNGP for app consumption and com.facebook.react.library for library consumption. This separation currently lives in RN Core, but it can be extracted. I need a bit of time to finalize the implementation and open a draft PR, after which we can align on the exact structure.

A few key points on this approach:

Backward Compatibility: I’ve kept this backward compatible for now, meaning libraries can still apply RNGP. Though whether we maintain this fallback is open for debate.

Plugin Integration: A clear benefit is that we can directly apply the kotlin-android-compat plugin inside com.facebook.react.library for libraries to use.

One caveat for AGP 9 compatibility: even with the compat plugin, library maintainers will still need to manually remove the kotlin-android plugin.


As part of Phase 2, the Expo team will evaluate required changes on their side, including ecosystem impact.

Phase 2 needs sufficient time, especially for community adoption. One possible support mechanism is adding an "AGP v9 compatible" filter in `react-native-community/directory` to track progress.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible I would avoid the extra filter in the directory (that should be doable if we implement this in being fully backward compat).

m-bert added a commit to software-mansion/react-native-gesture-handler that referenced this pull request Aug 13, 2026
## Description

<!--
Description and motivation for this PR.

Include 'Fixes #<number>' if this is fixing some issue.
-->

This PR is rasied following the second phase of the RFC around AGP v9
adoption:

RFC:
react-native-community/discussions-and-proposals#1006

The gist of this PR is to make Gesture Handler AGP v9 compliant with
backward compatibility. The main scope of changes is the
`react-native-gesture-handler/android/build.gradle`. The rest of the
changes can be considered temporary.

The rest changes include:

- Making Basic Example App AGP9 compliant
- Upgrade `Gradle` to `v9.4.1`
- Use `proguard-android-optimize` proguard file
- Enable opt outs in the `gradle.properties`

Ideally, we should not enable the opt outs and leverage the AGP 9
built-in kotlin and newDSL. However, if we do not do it, then other
libraries which are not yet AGP v9 compliant starts to fail. Hence, we
need to keep the opt outs enabled for a while. For the context,
react-native starting from 0.87.x will ship with AGP v9 and opt outs
enabled by default for the new apps, which is the first phase of AGP v9
adoption.

With the second phase when the libraries starts adoption AGP v9, we can
eventually remove the opt outs from Basic Example.

## Test plan

<!--
Describe how did you test this change here.
-->

To test this PR, we need to do a few steps:

- Set the `agp` version to `9.2.1` and `kotlin` to `2.2.0` in
`react-native-gradle-plugin`
```diff
--- a/node_modules/@react-native/gradle-plugin/gradle/libs.versions.toml
+++ b/node_modules/@react-native/gradle-plugin/gradle/libs.versions.toml
@@ -1,10 +1,10 @@
 [versions]
-agp = "8.12.0"
+agp = "9.2.1"
 gson = "2.8.9"

-kotlin = "2.1.20"
+kotlin = "2.2.0"
 assertj = "3.25.1"
```

- Comment out the following functions in `react-native-gradle-plugin`
```diff
--- a/node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/ReactPlugin.kt
+++ b/node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/ReactPlugin.kt
@@ -1,10 +1,10 @@
      configureBuildTypesForApp(project)
    }

    // Library Only Configuration
-    configureBuildConfigFieldsForLibraries(project)
-    configureNamespaceForLibraries(project)
+    // configureBuildConfigFieldsForLibraries(project)
+    // configureNamespaceForLibraries(project)
    project.pluginManager.withPlugin("com.android.library") {
```

This step is only required because we have RN version on 0.85.2 and
`AGP` + `kotlin` bump will be shipped with 0.87.x.

<details>
<summary>Verified by locally patching Reanimated and Worklet, making
those AGP v9 compliant and removing the opt outs to test the changes in
the PR </summary>



https://github.com/user-attachments/assets/7f4f571a-6bef-4694-9b32-330571c0c734



</details>


<details>
<summary>Verified these changes work with AGP 8 </summary>



https://github.com/user-attachments/assets/116a0b2e-23f5-499c-8a64-b99f927165c4




</details>

---------

Co-authored-by: Michał <michal.bert@swmansion.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants