skip to content

How do you apply a plugin using a version catalog (libs.versions.toml) with alias(libs.plugins.x), and how is the version wired in?

level: middleimportance: must knowfreq 60%

answer

  1. [plugins] id + version.ref
  2. alias(libs.plugins.x)
  3. kebab → camelCase accessor
  4. shared version.ref keeps lockstep
  5. catalog read at settings time

basics

~10 s

Define the plugin under [plugins] in gradle/libs.versions.toml (id + version, version can reference [versions]). Then in plugins {} write alias(libs.plugins.x). Gradle generates the typesafe libs accessor and supplies the version from the catalog.

solid answer

~40 s

A **version catalog** is a TOML file (conventionally `gradle/libs.versions.toml`) that centralizes dependency and plugin coordinates. Under `[plugins]` you declare each plugin with an `id` and a `version` (or `version.ref` pointing into `[versions]`). Gradle auto-generates a typesafe accessor named `libs` (the catalog's default name), so in any `build.gradle.kts` you apply the plugin with `alias(libs.plugins.springBoot)` inside `plugins {}`. The TOML key `spring-boot` maps to the accessor `libs.plugins.springBoot` (kebab/underscore segments become nested camelCase). Because the version lives in the catalog, build scripts carry no literal versions, and the same `version.ref` can be shared by a plugin and its companion dependencies — keeping, e.g., the Kotlin plugin and Kotlin stdlib in lockstep. The catalog is read very early (it's a settings-level feature), which is why it can feed the early `plugins {}` block.

code

toml · 11 lines
toml
# gradle/libs.versions.toml
[versions]
springBoot = "3.3.0"

[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" }

# build.gradle.kts
# plugins {
#     alias(libs.plugins.springBoot)
# }

go deeper

for a junior

Recall that you declare under [plugins] and apply with alias(libs.plugins.x).

for a middle

Explain version.ref, the kebab→camelCase mapping, and why no literal version appears in scripts.

for a senior

Use shared version.ref to keep plugin+library in lockstep and justify a single catalog across modules.

for a principal

Treat the catalog as governance: one manifest, possibly published/shared across repos for org-wide version policy.

## What a version catalog is A **version catalog** is a single source of truth for versions, libraries, bundles, and plugins, written in TOML. By default Gradle reads `gradle/libs.versions.toml` and exposes it as the `libs` extension with typesafe, IDE-completable accessors. ```toml # gradle/libs.versions.toml [versions] kotlin = "2.0.0" springBoot = "3.3.0" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" } spotless = { id = "com.diffplug.spotless", version = "6.25.0" } ``` ## Applying via alias() In the `plugins {}` block you do **not** repeat the id or version — you reference the catalog entry: ```kotlin plugins { alias(libs.plugins.kotlinJvm) alias(libs.plugins.springBoot) alias(libs.plugins.spotless) apply false // alias still supports apply false } ``` ### Name mapping rules TOML keys use `-`/`_` as segment separators; accessors turn them into nested camelCase: - `kotlin-jvm` → `libs.plugins.kotlinJvm` - `spring-boot` → `libs.plugins.springBoot` ## Why version.ref matters `version.ref` points a plugin at an entry in `[versions]`. That same `[versions]` entry can also version libraries: ```toml [libraries] kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } ``` Now the Kotlin **plugin** and the Kotlin **stdlib** always move together — bump `kotlin` once. ## How the version is wired into the early plugins block Catalogs are a settings-time feature: Gradle parses the TOML before scripts execute and synthesizes the `libs` accessor. That is what makes it legal to use `alias(libs.plugins.x)` inside the otherwise-restricted `plugins {}` block, where arbitrary expressions/variables are forbidden. The accessor is essentially a precomputed constant, not a runtime lookup. ## Sharing one catalog across modules The catalog declared in `settings.gradle.kts` (or the default file) is visible to **every** module, so plugin versions are defined once and applied everywhere by alias — the cleanest pattern for multi-module builds. ## Common gotcha `alias(libs.plugins.x)` is only valid for entries under **[plugins]**. Putting a plugin under `[libraries]` and aliasing it as a plugin fails. Likewise `libs.versions.kotlin` gives the version string, not something applicable as a plugin.

  • How does the TOML key spring-boot become an accessor in the build script?
    Gradle generates a typesafe `libs` accessor where `-`/`_`-separated segments become nested camelCase: `spring-boot` → `libs.plugins.springBoot`. It is `libs.plugins.<name>` for [plugins] entries.
  • Can you still use apply false with an alias?
    Yes. `alias(libs.plugins.x) apply false` resolves the version from the catalog without applying the plugin — useful for declaring versions at the root of a multi-project build.
  • Why can alias() appear in the early plugins {} block when variables can't?
    Version catalogs are parsed at settings time and the `libs` accessors are generated constants, not runtime expressions, so they satisfy the block's literal-only constraint.

The catalog is like a parts manifest: build scripts order parts by name (alias) and the manifest decides the exact version everyone gets.

saying these in an interview costs you the question

  • Putting the plugin under [libraries] and trying to alias it as a plugin.
  • Repeating the version literal in the script even though the catalog already carries it.
  • Assuming the accessor keeps the TOML's kebab-case (it becomes camelCase).

context