Room 3.0.0-rc01 landed on June 17, 2026, and it's not a point release with a quiet changelog. It's a new artifact, a new package, and a list of breaking changes long enough that "just bump the version" will not work for anyone shipping a real app. I've been running Room since 2.x was the only option across Nodat, Musist, and HailUp, and this is the first Room release where I'd genuinely tell a team to budget real migration time before touching it.
The short version: Room 3.0 drops Java and KAPT entirely, moves off Android's SupportSQLite/Cursor types in favor of the newer SQLiteDriver APIs, makes coroutines mandatory for every DAO function, and ships under a brand-new androidx.room3 namespace specifically so it can sit alongside Room 2.x without colliding. That last part matters more than it sounds — it's the difference between a flag day and a gradual migration.
1. A New Package, On Purpose
The first thing that'll surprise anyone expecting a normal major-version bump: Room 3.0 doesn't live in androidx.room at all. RoomDatabase, the annotations, the runtime — all of it moved to androidx.room3.
dependencies {
val room_version = "3.0.0-rc01"
implementation("androidx.room3:room3-runtime:$room_version")
ksp("androidx.room3:room3-compiler:$room_version")
}
This isn't an accident or a rebrand for its own sake. Libraries like WorkManager have a transitive dependency on Room, and a hard rename inside the same package would force every dependency in the graph to upgrade in lockstep. Splitting the namespace means Room 2.x and Room 3.x can both sit in your dependency graph at once. In practice that means you can migrate database-by-database or module-by-module instead of being forced into a single all-or-nothing cutover across the whole app — which is exactly the kind of incremental path you want on anything with more than one persistence layer.
2. KSP-Only — Java and KAPT Are Actually Gone
Room 2.7 turned on Kotlin code generation via KSP by default back in April 2025, but it kept generating Java sources underneath for KAPT and Java-only projects as a compatibility path. Room 3.0 removes that path entirely. The docs are blunt about it: "there is no Java code generation and configuration of annotation processor via KAPT or JavaAP is no longer possible in Room 3.0."
If any module in your app still configures Room through kapt instead of ksp, or if you have DAOs written in Java, Room 3.0 is a non-starter until that's fixed — full stop, not a deprecation warning. For most teams that have already been on KSP since 2.7, this changes nothing day-to-day. For teams still carrying KAPT for legacy reasons, this is the forcing function to finally finish that migration before Room 3.0 is even on the table.
3. No More SupportSQLite, No More Cursor
This is the change that touches the most code. Room 3.0 is "fully backed by the SQLiteDriver APIs and no longer references SupportSQLite types such as SupportSQLiteDatabase or Android types such as Cursor." If you ever dropped down to raw queries or manual transactions, the API surface is different:
// Room 2.x
roomDatabase.runInTransaction { ... }
roomDatabase.query("SELECT * FROM Song").use { cursor -> ... }
// Room 3.0
roomDatabase.withWriteTransaction { ... }
roomDatabase.useReaderConnection { connection ->
connection.usePrepared("SELECT * FROM Song") { stmt -> ... }
}
Anyone using Room purely through generated DAOs and annotated queries — the overwhelming majority of usage — won't touch this surface directly. But any custom RoomDatabase subclass, manual migration code, or a debug/inspection tool that reached into SupportSQLiteDatabase for direct access needs a rewrite, not a find-and-replace.
4. Coroutines Are No Longer Optional
Room 2.x let you write a blocking DAO function if you really wanted to, and configure the database with a plain Executor. Room 3.0 removes both options. "Room 3.0 also requires the use of Coroutines, and more specifically DAO functions have to be suspending unless they are returning a reactive type, such as a Flow or a custom DAO return type." There's no more Executor parameter on the builder either — you configure a CoroutineContext and dispatcher instead.
Honestly, this is the change I have the least sympathy for as a complaint. By 2026 I'd be surprised if any production Room DAO in a serious codebase wasn't already suspend or returning Flow — it's been the default recommendation for years. This mostly formalizes existing best practice rather than breaking working code, unless you've got an old synchronous DAO function nobody got around to migrating.
5. InvalidationTracker Is Flow-Based Now
The observer-callback pattern is gone. InvalidationTracker.Observer, along with addObserver/removeObserver, has been removed in favor of a Flow-returning createFlow() API:
fun getArtistTours(from: Date, to: Date): Flow<Map<Artist, TourState>> {
return db.invalidationTracker.createFlow("Artist").map { _ ->
val artists = artistsDao.getAllArtists()
val tours = tourService.fetchStates(artists.map { it.id })
associateTours(artists, tours, from, to)
}
}
If you ever hand-wired invalidation observers to react to table changes outside of a standard @Query return type — composing results across DAOs, like the tour/artist example above — this is now a one-line Flow chain instead of manual observer bookkeeping. It's a genuine improvement, but it's also a hard break for anyone using the old observer API directly.
The pattern across all five changes above: Room 3.0 isn't adding ceremony for its own sake — it's deleting every escape hatch back to blocking, Java-shaped, or callback-based code. If your codebase has already fully committed to Kotlin, coroutines, and Flow, the actual surface area you need to touch is smaller than the changelog makes it look.
What You Get For the Trouble
The breaking changes are the headline, but the new capabilities are why I'd actually bother migrating rather than just sitting on 2.8.4 indefinitely:
- Connection pool control. New builder functions,
setSingleConnectionPool()andsetMultipleConnectionPool(), let you cap how many connections Room opens against the database — useful if you've ever had to reason about write contention on a heavily-used local cache, which is exactly the kind of thing Musist's offline media metadata layer runs into under load. - Native FTS5 support. A new
@Fts5annotation withTOKENIZER_ASCII/TOKENIZER_TRIGRAMconstants and a detail-level enum (FULL,COLUMN,NONE) — full-text search without hand-rolling virtual table setup, which used to mean dropping out of Room's annotation model entirely. - Custom DAO return types. A
@DaoReturnTypeConverterannotation lets a DAO function return any type you define, transforming the raw query result yourself — handy for wrapping results in aPagingSourceor a domain-specific result type without a manual mapping layer downstream. - JS and WasmJs as KMP targets. Combined with a new
WebWorkerSQLiteDriverin theandroidx.sqlite:sqlite-webartifact, Room can now run in common code targeting web alongside Android, iOS, and desktop — relevant the moment any of your shared business logic needs to reach a browser. - Composite relationship keys.
@Relationand@Junctionnow accept arrays of column names forparentColumns/entityColumns, so relationships keyed on more than one column no longer need a workaround.
FTS5 support alone would have saved me a chunk of custom virtual-table setup on a past search feature. If full-text search is on your roadmap, that's a real reason to track this release rather than waiting it out passively.
Should You Migrate Now?
Room 3.0 is at rc01, not stable — the alpha cycle started March 11, 2026 and it's moved through the usual sequence of feature-adding alphas into release candidate. Room 2.x is still very much alive and maintained; 2.8.4 shipped as recently as November 2025. There's no forcing function to move today, and "rc" still means the API surface could see minor adjustments before final stable.
What I'd actually do: if you're starting a new module or a new app today, build it on Room 3.0 directly — there's no legacy code to break, and you get the full feature set from day one. For existing production apps, this is a good moment to audit whether you have any lingering KAPT, Java DAOs, or blocking database calls, since those are exactly what blocks the eventual migration regardless of when you do it. Fix those now under Room 2.x, where they're low-risk changes, so that when Room 3.0 goes stable the actual migration is closer to a dependency-and-import swap than a rewrite.
The namespace split is the detail I'd hold onto here. Most "3.0" library releases force a hard cutover the day you bump the version. Room's approach — new package, explicit coexistence, no surprise breakage for transitive dependents like WorkManager — is the kind of migration design that respects how actual production codebases evolve. It's worth modeling the next time you're versioning a library of your own.
No comments yet. Be the first to leave one!