skip to content

What is a Gradle DSL extension, and how do you register one from a plugin so users can configure your plugin in their build script?

level: juniorimportance: must knowfreq 70%

answer

  1. extensions.create(name, Class)
  2. ExtensionContainer on Project
  3. block name = extension name
  4. Gradle instantiates via ObjectFactory
  5. abstract class + managed properties

basics

~10 s

An extension is an object added to a project that exposes a configuration block (DSL). A plugin registers it with project.extensions.create("myExt", MyExtension::class.java), then users configure it via a myExt { ... } block.

solid answer

~40 s

A DSL extension is a plain object Gradle attaches to a container (usually the `Project`) so that a `myExt { ... }` block appears in the user's build script. In a plugin's `apply` method you call `project.extensions.create("myExt", MyExtension::class.java)`. The name becomes the DSL block name; the class defines the configurable surface. Gradle instantiates the class for you (so it can inject services), holds the single instance in the project's `ExtensionContainer`, and routes the `myExt { ... }` block to it. The plugin later reads the configured values — typically during task registration — to drive behavior. Extensions are the idiomatic way to expose plugin configuration; you should not read raw project properties or invent your own global state.

code

kotlin · 14 lines
kotlin
abstract class GreetingExtension {
    abstract val message: Property<String>
}

class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.extensions.create("greeting", GreetingExtension::class.java)
    }
}

// build.gradle.kts (consumer)
greeting {
    message.set("Hello")
}

go deeper

for a junior

Know that an extension adds a named configuration block and that you register it with extensions.create(name, Class) inside a plugin.

for a middle

Explain that Gradle instantiates the class (enabling injection/managed properties) and that the create name drives the DSL block, distinct from the class name.

for a senior

Discuss lifecycle: extension created at apply time but values populated during evaluation, so configuration must be consumed lazily; contrast with reading raw properties.

for a principal

Frame extensions as the public API of a plugin — naming, stability, and encapsulation matter; design the surface for discoverability and forward compatibility across plugin versions.

## What an extension is Gradle build scripts are Kotlin/Groovy code, but blocks like `application { }`, `java { }`, or your own `myExt { }` are not language keywords — they are **extensions**. An extension is just an object that a plugin adds to a Gradle container (most commonly the `Project`). Every `Project` has an `ExtensionContainer` accessible as `project.extensions`. When you add an object under a name, Gradle exposes a configuration block with that name in the DSL. ## Registering one The canonical call is: ```kotlin project.extensions.create("myExt", MyExtension::class.java) ``` This does three things: (1) it **instantiates** `MyExtension` using Gradle's `ObjectFactory`, so the class can have services injected and managed properties created; (2) it **stores** that single instance in the `ExtensionContainer` under the name `myExt`; and (3) it makes a `myExt { ... }` block available in the build script that configures that instance. Use `create` rather than `new MyExtension()` + `add` — letting Gradle construct the object is what enables managed properties and dependency injection. ## The extension class A modern extension is an **abstract class** with abstract getters for managed properties: ```kotlin abstract class MyExtension { abstract val message: Property<String> abstract val tags: ListProperty<String> } ``` Gradle synthesizes the implementations of `message` and `tags`, backing them with lazy `Property`/`ListProperty` objects. The plugin reads them later, e.g. when registering a task. ## Why extensions, not properties Extensions give users a typed, discoverable, IDE-completable configuration surface; they integrate with Gradle's lazy configuration model; and they keep plugin state encapsulated instead of leaking into ad-hoc `project.ext`/gradle.properties reads. ## Lifecycle note The extension object is created at apply time, but its values are populated as the build script is evaluated. So a plugin must **not** read final values eagerly in `apply` — it should wire them lazily (via providers) so the user's configuration is seen.

  • Where is the extension instance stored, and how would you retrieve it later in the same plugin?
    In the project's ExtensionContainer. You can capture the return value of create(...), or fetch it later with project.extensions.getByType(MyExtension::class.java) / findByName("myExt").
  • Why call extensions.create instead of constructing the class yourself and calling add?
    create lets Gradle instantiate the object via ObjectFactory, which enables service injection and managed (abstract) properties. A hand-constructed instance has no injected ObjectFactory, so abstract Property getters won't be implemented.

An extension is like a settings panel your plugin bolts onto the build: create mounts the panel and labels it, and the user fills in the fields through the named block.

saying these in an interview costs you the question

  • Saying the block name (myExt) must match the class name — it doesn't; the name is the first argument to create.
  • Claiming you should read configuration via project.property/gradle.properties instead of an extension.

context