# Introduction to Plugins

version 9.5.0-20260402015527+0000

Gradle comes with a set of powerful core systems such as dependency management, task execution, and project configuration. But everything else it can do is supplied by plugins.

Plugins encapsulate logic for specific tasks or integrations, such as compiling code, running tests, or deploying artifacts. By applying plugins, users can easily add new features to their build process without having to write complex code from scratch.

This plugin-based approach allows Gradle to be lightweight and modular. It also promotes code reuse and maintainability, as plugins can be shared across projects or within an organization.

Before reading this chapter, it’s recommended that you first complete the Advanced Tutorial and Reading.

## Sources for Plugins

Plugins can be sourced from Gradle or the Gradle community. But when users want to organize their build logic or need specific build capabilities not provided by existing plugins, they can develop their own.

As such, we distinguish between three different sources for Gradle plugins:

1. Core Plugins - plugins that come from Gradle itself.

2. Community Plugins - plugins that come from the Gradle Plugin Portal or a public repository.

3. Local or Custom Plugins - plugins that you develop yourself.

### Core Plugins

The term core plugin refers to a plugin that is part of the Gradle distribution such as the Java Library Plugin. They are always available.

### Community Plugins

The term community plugin refers to a plugin published to the Gradle Plugin Portal (or another public repository) such as the Spotless Plugin.

### Local or Custom Plugins

The term local plugin or custom plugin refers to a plugin you write yourself for your own build.

## Types of Plugins

There are three types of plugins you can develop:

# | Type | Location: | Most likely: | Benefit:
--- | --- | --- | --- | ---
1 | Script plugins | A `.gradle(.kts)` script file | A local plugin | The plugin is automatically compiled and included in the classpath of the build script.
2 | Precompiled script plugins | `buildSrc` folder or composite build | A convention plugin | The plugin is automatically compiled, tested, and available on the classpath of the build script. The plugin is visible to every build script used by the build.
3 | Binary plugins | Standalone project | A shared plugin | The plugin JAR is produced and can be published. The plugin can be used in multiple builds and shared with others.

### Script plugins

A script plugin is a plugin written directly in a build file (`.gradle` or `.gradle.kts`). They are usually small, local utilities for one project and not intended for reuse.

Script plugins are useful for quick experiments but not recommended for production builds. They often evolve into proper plugins placed in `buildSrc` or published as standalone plugins.

The simplest plugin is a class that implements `Plugin<Project>` and configures the project when applied:

build.gradle.kts

    class GreetingPlugin : Plugin<Project> {
        override fun apply(project: Project) {
            project.task("hello") {
                doLast {
                    println("Hello from the GreetingPlugin")
                }
            }
        }
    }

    // Apply the plugin
    apply<GreetingPlugin>()

build.gradle

    class GreetingPlugin implements Plugin<Project> {
        void apply(Project project) {
            project.task('hello') {
                doLast {
                    println 'Hello from the GreetingPlugin'
                }
            }
        }
    }

    // Apply the plugin
    apply plugin: GreetingPlugin

    $ ./gradlew -q hello

    Hello from the GreetingPlugin

You can also put such a plugin in a separate script file and apply it with `apply(from = "other.gradle.kts")` or `apply from: 'other.gradle'`:

other.gradle.kts

    class GreetingScriptPlugin : Plugin<Project> {
        override fun apply(project: Project) {
            project.task("hi") {
                doLast {
                    println("Hi from the GreetingScriptPlugin")
                }
            }
        }
    }

    // Apply the plugin
    apply<GreetingScriptPlugin>()

other.gradle

    class GreetingScriptPlugin implements Plugin<Project> {
        void apply(Project project) {
            project.task('hi') {
                doLast {
                    println 'Hi from the GreetingScriptPlugin'
                }
            }
        }
    }

    // Apply the plugin
    apply plugin: GreetingScriptPlugin

build.gradle.kts

    apply(from = "other.gradle.kts")

build.gradle

    apply from: 'other.gradle'

    $ ./gradlew -q hi

    Hi from the GreetingScriptPlugin

Script plugins should be avoided. Script plugins are convenient for local, throwaway logic but make builds harder to maintain.

### Precompiled script plugins

Precompiled script plugins are compiled into class files and packaged into a JAR before they are executed. These plugins use the Gradle DSLs instead of pure Java, Kotlin, or Groovy.

To create a precompiled script plugin, you can:

1. Use Gradle’s Kotlin DSL - The plugin is a `.gradle.kts` file, and applies `id("kotlin-dsl")`.

2. Use Gradle’s Groovy DSL - The plugin is a `.gradle` file, and apply `id("groovy-gradle-plugin")`.

To apply a precompiled script plugin, you need to know its ID. The ID is derived from the plugin script’s filename and its (optional) package declaration.

For example, the script `src/main/kotlin/some-java-library.gradle.kts` has a plugin ID of `some-java-library` (assuming it has no package declaration). Likewise, `src/main/kotlin/my/some-java-library.gradle(.kts)` has a plugin ID of `my.some-java-library` as long as it has a package declaration of `my`.

Precompiled script plugin names have two important limitations:

- They cannot start with `org.gradle`.

- They cannot have the same name as a core plugin.

When the plugin is applied to a project, Gradle creates an instance of the plugin class and calls the instance’s `Plugin.apply()` method.

Let’s rewrite the `GreetingPlugin` script plugin as a precompiled script plugin. The script plugin is moved to its own file in `buildSrc`:

buildSrc/src/main/kotlin/GreetingPlugin.gradle.kts

    tasks.register("hello") {
        doLast {
            println("Hello from the convention GreetingPlugin")
        }
    }

buildSrc/src/main/groovy/GreetingPlugin.gradle

    tasks.register("hello") {
        doLast {
            println("Hello from the convention GreetingPlugin")
        }
    }

The `GreetingPlugin` can now be applied in other subprojects' builds by using its ID:

app/build.gradle.kts

    plugins {
        application
        id("GreetingPlugin")
    }

app/build.gradle

    plugins {
        id 'application'
        id('GreetingPlugin')
    }

    $ ./gradlew -q hello

    Hello from the convention GreetingPlugin

### Convention plugins

A convention plugin is typically a precompiled script plugin that configures existing core and community plugins with your own conventions (i.e. default values) such as setting the Java version by using `java.toolchain.languageVersion = JavaLanguageVersion.of(17)`.

Convention plugins are also used to enforce project standards and help streamline the build process. They can apply and configure plugins, create new tasks and extensions, set dependencies, and much more.

Let’s take an example build with three subprojects: one for `data-model`, one for `database-logic` and one for `app` code. The project has the following structure:

    .
    ├── buildSrc
    │   ├── src
    │   │   └──...
    │   └── build.gradle.kts
    ├── data-model
    │   ├── src
    │   │   └──...
    │   └── build.gradle.kts
    ├── database-logic
    │   ├── src
    │   │   └──...
    │   └── build.gradle.kts
    ├── app
    │   ├── src
    │   │   └──...
    │   └── build.gradle.kts
    └── settings.gradle.kts

The build file of the `database-logic` subproject is as follows:

database-logic/build.gradle.kts

    plugins {
        id("java-library")
        id("org.jetbrains.kotlin.jvm") version "2.3.20"
    }

    repositories {
        mavenCentral()
    }

    java {
        toolchain.languageVersion.set(JavaLanguageVersion.of(11))
    }

    tasks.test {
        useJUnitPlatform()
    }

    kotlin {
        jvmToolchain(11)
    }

    // More build logic

database-logic/build.gradle

    plugins {
        id 'java-library'
        id 'org.jetbrains.kotlin.jvm' version '2.3.20'
    }

    repositories {
        mavenCentral()
    }

    java {
        toolchain.languageVersion.set(JavaLanguageVersion.of(11))
    }

    tasks.test {
        useJUnitPlatform()
    }

    kotlin {
        jvmToolchain {
            languageVersion.set(JavaLanguageVersion.of(11))
        }
    }

    // More build logic

We apply the `java-library` plugin and add the `org.jetbrains.kotlin.jvm` plugin for Kotlin support. We also configure Kotlin, Java, tests and more.

Our build file is beginning to grow.

The more plugins we apply and the more plugins we configure, the larger it gets. There’s also repetition in the build files of the `app` and `data-model` subprojects, especially when configuring common extensions like setting the Java version and Kotlin support.

To address this, we use convention plugins. This allows us to avoid repeating configuration in each build file and keeps our build scripts more concise and maintainable. In convention plugins, we can encapsulate arbitrary build configuration or custom build logic.

To develop a convention plugin, you can use the protected `buildSrc` folder, which represents a completely separate Gradle build. `buildSrc` has its own settings file to define where dependencies of this build are located.

We add a Kotlin script called `my-java-library.gradle.kts` inside the `buildSrc/src/main/kotlin` directory. Or conversely, a Groovy script called `my-java-library.gradle` inside the `buildSrc/src/main/groovy` directory. We put all the plugin application and configuration from the `database-logic` build file into it:

buildSrc/src/main/kotlin/my-java-library.gradle.kts

    plugins {
        id("java-library")
        id("org.jetbrains.kotlin.jvm")
    }

    repositories {
        mavenCentral()
    }

    java {
        toolchain.languageVersion.set(JavaLanguageVersion.of(11))
    }

    tasks.test {
        useJUnitPlatform()
    }

    kotlin {
        jvmToolchain(11)
    }

buildSrc/src/main/groovy/my-java-library.gradle

    plugins {
        id 'java-library'
        id 'org.jetbrains.kotlin.jvm'
    }

    repositories {
        mavenCentral()
    }

    java {
        toolchain.languageVersion.set(JavaLanguageVersion.of(11))
    }

    tasks.test {
        useJUnitPlatform()
    }

    kotlin {
        jvmToolchain {
            languageVersion.set(JavaLanguageVersion.of(11))
        }
    }

The name of the file `my-java-library` is the ID of our plugin, which we can now reference in all of our subprojects.

| Why is the version of `id 'org.jetbrains.kotlin.jvm'` missing? See Applying External Plugins to Pre-Compiled Script Plugins.

The `database-logic` build file becomes much simpler by removing all the redundant build logic and applying our convention `my-java-library` plugin instead:

database-logic/build.gradle.kts

    plugins {
        id("my-java-library")
    }

database-logic/build.gradle

    plugins {
        id('my-java-library')
    }

This convention plugin enables us to easily share common configurations across all our build files. Any modifications can be made in one place, simplifying maintenance.

### Binary plugins

Binary plugins in Gradle are plugins that are built as standalone JAR files and applied to a project using the `plugins {}` block in the build script.

Let’s move our `GreetingPlugin` to a standalone project so that we can publish it and share it with others. The plugin is essentially moved from the `buildSrc` folder to its own build called `greeting-plugin`.

| You can publish the plugin from `buildSrc`, but this is not recommended practice. Plugins that are ready for publication should be in their own build.

`greeting-plugin` is simply a Java project that produces a JAR containing the plugin classes.

The easiest way to package and publish a plugin to a repository is to use the Gradle Plugin Development Plugin. This plugin provides the necessary tasks and configurations (including the plugin metadata) to compile your script into a plugin that can be applied in other builds.

Here is a simple build script for the `greeting-plugin` project using the Gradle Plugin Development Plugin:

build.gradle.kts

    plugins {
        `java-gradle-plugin`
    }

    gradlePlugin {
        plugins {
            create("simplePlugin") {
                id = "org.example.greeting"
                implementationClass = "org.example.GreetingPlugin"
            }
        }
    }

build.gradle

    plugins {
        id 'java-gradle-plugin'
    }

    gradlePlugin {
        plugins {
            simplePlugin {
                id = 'org.example.greeting'
                implementationClass = 'org.example.GreetingPlugin'
            }
        }
    }

For more on publishing plugins, see Publishing Plugins.

## Scope of Plugins

In earlier examples, `GreetingScriptPlugin` was defined against a `Project` parameter:

    class GreetingScriptPlugin : Plugin<Project> { }

Gradle plugins can in fact target three different scopes: `Project`, `Settings`, and `Gradle`. Each scope controls different aspects of the build lifecycle and represents an interface.

Interface | Scope | Description
--- | --- | ---
Project | Applied to an individual project. | Used to define build logic, register tasks, and configure project-specific settings.
Settings | Applied to the build as a whole. | Used to include subprojects, configure plugin management, or define buildscript repositories.
Gradle (init) | Applied globally. | Affects all builds on a machine. Commonly used to configure enterprise build tooling, inject repositories, or enforce conventions across builds.

When writing precompiled script plugins, Gradle determines the intended plugin target (`Project`, `Settings`, or `Gradle`) based on the file name of the plugin source file:

Plugin | Filename Suffix | Applies To | Scope
--- | --- | --- | ---
Project plugin | `.gradle`, `.gradle.kts` | `Plugin<Project>` object | Project scope
Settings plugin | `.settings.gradle`, `.settings.gradle.kts` | `Plugin<Settings>` object | Build scope
Init plugin | `.init.gradle`, `.init.gradle.kts` | `Plugin<Gradle>` object | Global scope

For example, a file named `myplugin.gradle.kts` will always be treated as a Project plugin—even if it’s applied from a `settings.gradle.kts` file. To create a plugin for the settings context, it must be named something like `myplugin.settings.gradle.kts`.