skip to content

What is a Gradle version catalog (`gradle/libs.versions.toml`), and how do you declare a Kotlin plugin in it and reference it with `alias(libs.plugins.x)`?

level: middleimportance: must knowfreq 60%

answer

  1. gradle/libs.versions.toml, auto-detected, accessor = libs
  2. Tables: versions, libraries, plugins, bundles
  3. Plugins: { id = ..., version.ref = ... }
  4. alias(libs.plugins.kotlin.jvm) in plugins {}
  5. Dash in TOML key -> dot in accessor

basics

~20 s

A version catalog is a TOML file that centralizes your dependency and plugin versions in one place. You name a plugin there, then apply it in a build script with alias(libs.plugins.someName) instead of repeating the id and version.

solid answer

~40 s

`gradle/libs.versions.toml` is Gradle's built-in **version catalog**. It has `[versions]`, `[libraries]`, `[plugins]`, and `[bundles]` tables. A plugin entry looks like `kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }`. Gradle generates a type-safe accessor named `libs` (the catalog file's basename), so in `build.gradle.kts` you write `alias(libs.plugins.kotlin.jvm)` inside `plugins {}` — note dashes in TOML keys map to dots/camelCase in code. `alias()` is the only catalog-aware function allowed in the declarative block. For libraries you use `implementation(libs.someLib)` in `dependencies {}`. The catalog is auto-detected at `gradle/libs.versions.toml`; you can add more via `settings.gradle.kts` `versionCatalogs {}`. Benefit: single source of truth, no version drift across modules.

code

kotlin · 15 lines
kotlin
// gradle/libs.versions.toml
// [versions]
// kotlin = "2.0.20"
// [plugins]
// kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

// build.gradle.kts
plugins {
    alias(libs.plugins.kotlin.jvm)
}

dependencies {
    implementation(libs.ktor.server.core)
    testImplementation(libs.bundles.testing)
}

go deeper

for a junior

Knows the catalog centralizes versions and you reference plugins with alias(libs.plugins.x).

for a middle

Explains the four TOML tables, version.ref, accessor generation, and the dash-to-dot naming rule.

for a senior

Uses catalogs with apply false to kill version drift across modules and knows how to add custom catalogs in settings.

for a principal

Designs the catalog as the org-wide single source of truth, possibly published/shared, and reasons about its interaction with convention plugins and the configuration cache.

## What a version catalog is A **version catalog** is a Gradle feature that centralizes the coordinates and versions of dependencies and plugins so every module references the same definitions. The conventional file is `gradle/libs.versions.toml` (checked into the repo). Gradle auto-detects it and exposes a type-safe accessor object named after the file: `libs`. ## The TOML structure Four tables: - `[versions]` — named version strings you can reference. - `[libraries]` — dependency coordinates (`group:name`), usually with `version.ref`. - `[plugins]` — plugin ids with versions, used via `alias(...)`. - `[bundles]` — named groups of libraries applied together. ```toml [versions] kotlin = "2.0.20" ktor = "2.3.12" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } [libraries] ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } [bundles] ktor = ["ktor-server-core"] ``` ## Referencing in `build.gradle.kts` ```kotlin plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.serialization) } dependencies { implementation(libs.ktor.server.core) // or a bundle: implementation(libs.bundles.ktor) } ``` ### Naming rules (the common gotcha) - A TOML key like `kotlin-jvm` becomes the accessor `libs.plugins.kotlin.jvm` — the **dash splits into a sub-accessor (dot)**. - Dots and dashes are normalized; `kotlin.jvm` and `kotlin-jvm` collide. Keep keys kebab-case for predictable accessors. - Plugins are reached under `libs.plugins.*`, libraries under `libs.*`, bundles under `libs.bundles.*`, raw versions under `libs.versions.*`. ### Why `alias()` specifically Inside the restricted `plugins {}` block, `alias(libs.plugins.x)` is the **only** way to pull a plugin from the catalog; you cannot write `id(libs.plugins.x.get().pluginId)` style code there because the block is declarative. ## `apply false` with catalogs In a root build script you often write `alias(libs.plugins.kotlin.jvm) apply false` to put a plugin on the classpath with a single version, then `alias(libs.plugins.kotlin.jvm)` in each subproject (no version needed, since the catalog supplies it). This eliminates version drift. ## Multiple / custom catalogs The `libs` catalog is auto-detected. You can declare additional catalogs (or rename) in `settings.gradle.kts`: ```kotlin dependencyResolutionManagement { versionCatalogs { create("testLibs") { from(files("gradle/test.versions.toml")) } } } ```

  • How does the TOML key `kotlin-jvm` become an accessor in Kotlin DSL?
    Gradle splits on the dash, so `kotlin-jvm` becomes `libs.plugins.kotlin.jvm`. Dashes and dots are normalized to nested accessors, which is why kebab-case keys are recommended.
  • Why use `version.ref` instead of inlining the version on each entry?
    `version.ref` points multiple entries at one `[versions]` value (e.g. all Kotlin artifacts share `kotlin`), so a single edit bumps them together and prevents mismatched versions.

A version catalog is the build's single 'shopping list' pinned to the fridge — every room (module) reads the same list, so nobody buys a different version of milk.

saying these in an interview costs you the question

  • Putting the version on the plugin in the script when the catalog already supplies it (causes a conflict error)
  • Not knowing dashes map to dotted sub-accessors
  • Thinking the catalog file can live anywhere by default (it must be `gradle/libs.versions.toml` to be auto-detected)
  • Using `id(...)` with a string instead of `alias(libs.plugins.x)` and losing the central version
  • Confusing `[libraries]` (dependencies) with `[plugins]` (applied via alias)

context