Catalogs keep versions consistent across subprojects and are the modern standard in Android and JVM builds.
A version catalog declares dependency coordinates and versions once, in gradle/libs.versions.toml, and every build script references them through the type-safe libs accessor.
[versions]
junit = "5.11.4"
kotlin = "2.1.20"
[libraries]
guava = { module = "com.google.guava:guava", version = "33.4.0-jre" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
[bundles]
testing = ["junit-jupiter"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }Then implementation(libs.guava), testImplementation(libs.bundles.testing) and plugins { alias(libs.plugins.kotlin.jvm) }. Dashes in names become dots in accessors.
Benefits: one place to bump versions, IDE completion, no string typos, and tooling such as Renovate and Dependabot understands the file. version.ref shares a version among related artifacts (all Kotlin or all Jackson modules), and bundles group libraries that are always used together.
The catalog named libs is picked up automatically; additional catalogs can be declared in dependencyResolutionManagement { versionCatalogs { } } in settings.gradle.kts, or published as a .toml artifact for sharing across repositories.
Catalogs don't change resolution rules: they only provide coordinates. Use platforms/BOMs (implementation(platform(libs.spring.bom))) when you need to align transitive versions.
Example
# gradle/libs.versions.toml
[versions]
junit = "5.11.4"
jackson = "2.18.3"
[libraries]
guava = { module = "com.google.guava:guava", version = "33.4.0-jre" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
[bundles]
jackson = ["jackson-databind", "jackson-kotlin"]
[plugins]
spring-boot = { id = "org.springframework.boot", version = "3.4.4" }
# build.gradle.kts
# plugins { alias(libs.plugins.spring.boot) }
# dependencies {
# implementation(libs.guava)
# implementation(libs.bundles.jackson)
# testImplementation(libs.junit.jupiter)
# }Remember
- gradle/libs.versions.toml holds versions, libraries, bundles and plugins
- Use libs.xxx accessors; dashes become dots
- version.ref shares one version across related artifacts
- Catalogs provide coordinates; BOMs/platforms align transitive versions