Version Catalogs: One Source of Truth for Dependencies
Centralize every version, library, plugin, and bundle in gradle/libs.versions.toml and reference them type-safely as libs.xxx.
A version catalog is Gradle's built-in mechanism for declaring your dependencies and their versions in a single, shareable file instead of scattering coordinate strings and version numbers across every build.gradle.kts in your project. The catalog lives at gradle/libs.versions.toml (note: the gradle/ folder, not the root) and Gradle discovers it automatically โ no plugin and no configuration in settings.gradle.kts are required for the default catalog named libs. From that file Gradle generates a type-safe accessor object you reference in your build scripts as libs.something, giving you autocompletion in the IDE and a compile-time error the moment a name is misspelled or removed.
The TOML file is organized into four tables. [versions] holds named version strings (or rich version constraints) that you can reuse, e.g. kotlin = "2.1.0". [libraries] declares each dependency by its group, name, and a version reference back into [versions] (or an inline version). [plugins] declares Gradle plugins with their id and version, which you then apply through the plugins { } block. [bundles] groups several library aliases under one name so you can pull in a related set โ say all of Ktor, or all of your test libraries โ with a single line. Every table is optional, but together they describe your whole dependency surface in one place.
Aliases in the catalog use dashes, dots, or underscores as separators, and Gradle maps those to a nested, camelCased accessor. An alias like androidx-core-ktx becomes libs.androidx.core.ktx, and kotlinx-coroutines-core becomes libs.kotlinx.coroutines.core. Versions are reached through libs.versions.<name> (for example libs.versions.kotlin.get() when you need the raw string), bundles through libs.bundles.<name>, and plugins through libs.plugins.<name>. This consistent, discoverable naming is a big part of why catalogs feel so much nicer than hand-typed coordinate strings.
Centralizing versions matters most as a project grows. When the same library appears in several modules, a catalog guarantees every module uses the exact same version, eliminating the subtle, hard-to-debug conflicts that arise when one module quietly drifts to a different release. Upgrading becomes a one-line change in libs.versions.toml that instantly propagates everywhere, and a single version reference can drive several related artifacts โ bump the kotlin version once and the compiler plugin, the standard library, and the reflection library all move together. The catalog also becomes living documentation: a reviewer can read one file and know exactly what the project depends on.
Catalogs shine in multi-module builds because the generated libs accessor is automatically available in every module's build script and even in buildSrc / included builds, so there is nothing to wire up per module. If you maintain several repositories, you can publish a catalog as its own artifact and import it via a versionCatalogs block in settings.gradle.kts, sharing one curated set of approved versions across an entire organization. Tooling also understands the format โ the Gradle 'versions' tooling and Dependabot/Renovate can read and update libs.versions.toml directly.
A few practical tips: keep alias names descriptive and stable since they are part of your build's public surface; prefer version references over inline versions so a version is defined exactly once; use bundles for cohesive groups but don't over-bundle unrelated dependencies; and remember that the catalog only declares coordinates โ it does not put anything on a classpath until you actually reference an alias in a dependencies { } or plugins { } block. Because Gradle builds run outside this in-browser runner, treat the snippets below as the canonical, copy-ready shape for a modern Gradle 8 + Kotlin 2.x project.
# gradle/libs.versions.toml โ the single source of truth for versions[versions]kotlin = "2.1.0"ktor = "3.0.3"kotlinx-coroutines = "1.10.1"junit = "5.11.4"[libraries]# group:name resolved with a version pulled from [versions]ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }[plugins]# applied via the plugins { } block, not dependencieskotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }ktor = { id = "io.ktor.plugin", version.ref = "ktor" }[bundles]# one alias that expands to several libraries at oncektor-server = ["ktor-server-core", "ktor-server-netty"]
Four tables: [versions], [libraries], [plugins], [bundles] โ versions defined once and reused via version.ref.
// build.gradle.kts โ reference catalog entries type-safely as libs.xxxplugins {// dashes/dots in aliases become nested accessorsalias(libs.plugins.kotlin.jvm)alias(libs.plugins.ktor)}dependencies {// a bundle pulls in every library it groupsimplementation(libs.bundles.ktor.server)implementation(libs.kotlinx.coroutines.core)// a single library aliastestImplementation(libs.junit.jupiter)}// need the raw version string? reach it through libs.versionsval kotlinVersion = libs.versions.kotlin.get()
alias(...) for plugins, libs.bundles.x for groups, libs.x.y for single libs, libs.versions.x.get() for the string.
// settings.gradle.kts โ the default 'libs' catalog needs NO config here.// You only add this block to import an EXTERNAL/published catalog.dependencyResolutionManagement {versionCatalogs {create("shared") {// share one curated set of versions across repositoriesfrom("com.mycompany.platform:catalog:1.4.0")}}}
gradle/libs.versions.toml is auto-detected; this block is only for importing a shared/published catalog.
๐ง Check your understanding
0/1 ยท 0/1 answered1. In build.gradle.kts, how do you reference a library declared in libs.versions.toml with the alias kotlinx-coroutines-core?