skip to content

How do you apply plugins in build.gradle.kts using the plugins {} block, and what makes that block special?

level: juniorimportance: must knowfreq 65%

answer

  1. plugins { id("...") version "..." }
  2. core accessors: java, application
  3. kotlin("jvm") helper
  4. resolved early — literals only
  5. apply false for version-only

basics

~10 s

Use the plugins {} block: list plugins by id and version, e.g. id("org.springframework.boot") version "3.x", or use built-in accessors like kotlin("jvm") or java. It applies plugins declaratively at the top of the script.

solid answer

~40 s

The `plugins {}` block is the modern, declarative way to apply plugins. Inside it you write `id("plugin.id") version "x.y.z"` for community plugins, or use shorthand accessors for core plugins like `java`, `application`, or `kotlin("jvm") version "..."`. What makes the block special is that it is **resolved very early**, before the rest of the script is configured, and it must contain only literal declarations — no arbitrary logic, conditionals, or variables. Gradle parses it ahead of compiling the body so it knows which plugins (and thus which type-safe accessors and extensions) to make available. Versions can be centralised in a version catalog (`alias(libs.plugins.spring.boot)`) or the `pluginManagement` block in settings. This is preferred over the legacy `apply(plugin = "...")` because it gives type-safe accessors and better plugin resolution.

code

kotlin · 5 lines
kotlin
plugins {
    java
    kotlin("jvm") version "2.0.0"
    id("org.springframework.boot") version "3.3.0" apply false
}

go deeper

for a junior

Show the basic id(...) version "..." form and a core accessor like java.

for a middle

Explain early resolution, the literals-only restriction, kotlin("jvm"), and apply false.

for a senior

Connect early plugin resolution to type-safe accessor generation; discuss version catalogs and pluginManagement.

for a principal

Standardise plugin/version governance across a multi-module org via catalogs and convention plugins, minimising drift.

## The plugins {} block Plugins add capabilities to a build — the `java` plugin adds compile/test/jar tasks, the `application` plugin adds a `run` task, and so on. The `plugins {}` block is the **declarative** mechanism for applying them in the Kotlin DSL. ### Forms inside the block ```kotlin plugins { java // core plugin, accessor form application // another core plugin kotlin("jvm") version "2.0.0" // kotlin("...") helper id("org.springframework.boot") version "3.3.0" // community plugin by id id("io.spring.dependency-management") // version inherited / from catalog } ``` - `id("...")` — applies a plugin by its published id; `version "..."` pins it. - `kotlin("jvm")` — a helper that expands to `id("org.jetbrains.kotlin.jvm")`. - Bare accessors like `java` / `application` — generated for **core** Gradle plugins. - `apply false` — declares a plugin's version without applying it here (useful in a root script to set versions for subprojects). ### Why the block is special / restricted Gradle evaluates `plugins {}` **before** it compiles and runs the rest of the script. To do that it must be able to read the block statically, so the block accepts only **constant, literal** declarations — you cannot put `if` statements, loops, local variables, or calls to other functions inside it. This early resolution is exactly what lets Gradle generate **type-safe accessors**: once it knows the `application` plugin is applied, it can expose the `application { }` extension with full typing in the IDE. ### Versions and catalogs Versions can live inline, in `settings.gradle.kts`'s `pluginManagement { }`, or in a version catalog: ```kotlin plugins { alias(libs.plugins.spring.boot) } ``` ### Legacy alternative The old `apply(plugin = "java")` (or `buildscript {}` + `apply`) still works for edge cases (e.g. applying a plugin conditionally), but it does **not** yield type-safe accessors and is discouraged for normal use.

  • Why can't you put an if-statement inside the plugins {} block?
    Gradle resolves the block statically and very early, before compiling the script body, so it only accepts literal declarations — no logic, variables, or conditionals.
  • What does `apply false` do in a plugins block?
    It declares (and resolves the version of) the plugin without applying it in the current script, typically in a root build so subprojects can apply it without repeating the version.
  • How does plugins {} differ from the legacy apply(plugin = "...")?
    plugins {} resolves declaratively, enables type-safe accessors, and integrates with plugin resolution; apply(...) is imperative and gives no typed accessors.

saying these in an interview costs you the question

  • Putting conditionals or variables inside plugins {} and expecting it to work.
  • Confusing apply false (don't apply now) with not declaring the plugin at all.

context