Guide
How to make a Minecraft plugin in Kotlin
Updated October 18, 2026
Kotlin runs on the JVM, so a Kotlin plugin uses the same Paper or Spigot API, the same plugin.yml and the same plugins/ folder as a Java one. The code is shorter and null-safe. The one thing Java plugins do not have to worry about is the Kotlin standard library: the server does not ship it, so the plugin has to bring it.
Start from this example.
Generate this with CodexeStep by step
1.Create a Gradle project with Kotlin DSL
A
build.gradle.ktswith thekotlin("jvm")plugin, a Java toolchain that matches your Minecraft version (17, 21 or 25) and the PaperMC repository.2.Add the server API
compileOnly("io.papermc.paper:paper-api:<version>"). Like in Java, the server provides it at runtime, so it is never bundled.3.Write the main class
class MyPlugin : JavaPlugin()withoverride fun onEnable(). Register commands and listeners exactly as in Java.4.Bring the Kotlin standard library
Bundle it into the .jar with the Shadow plugin, or let the server download it at startup through the
librarieslist inplugin.yml. Without one of them the plugin fails withNoClassDefFoundError: kotlin/....5.Write plugin.yml
The same file as a Java plugin: name, version, main (the Kotlin class),
api-version, commands and permissions.6.Build and test
./gradlew shadowJar(orbuild), copy the .jar frombuild/libs/to a test server and read the console on startup.
Getting Kotlin onto the server
- Shade it: the Shadow Gradle plugin copies the Kotlin standard library and other runtime libraries into your .jar. It works on every version and is the most common choice.
- libraries in plugin.yml: from Spigot 1.16.5, a plugin can list Maven Central libraries that the server downloads and loads for it. The .jar stays small, but the server needs internet access on first start.
- Libraries you add: kotlinx-coroutines is a separate library, not part of the standard library, and must be bundled too. Relocating it into your own package avoids clashes with other plugins that bundle another version.
Coroutines and the main thread
Coroutines are a clean way to load data from a database without freezing the server, but the Bukkit API (worlds, entities, inventories) must still be used from the main thread. Do the slow work on a background dispatcher, then switch back to the server thread with the scheduler before touching the game. Tie the coroutine scope to the plugin, so it is cancelled in onDisable().
Java or Kotlin?
| Feature | Java | Kotlin |
|---|---|---|
| API | Paper / Spigot | The same Paper / Spigot API |
| Build | Maven or Gradle | Gradle with build.gradle.kts |
| Runtime extras | None | Kotlin standard library, bundled or loaded |
| Tutorials and examples | Most of them | Fewer, but Java examples translate directly |
With Codexe
Choose Bukkit/Spigot/Paper (Kotlin). Codexe generates a Gradle project with build.gradle.kts, the Paper API, the Shadow plugin to bundle what the server does not provide, and coroutines bundled and relocated when the code uses them. It compiles the plugin before it is Ready, and Kotlin plugins can be tried on a test server and checked by test players like Java ones. See the Kotlin plugin generator.
"A Paper 1.21.4 plugin in Kotlin called Bounties: /bounty set <player> <amount> through Vault, the killer gets the reward, /bounty top shows the 10 highest, stored in SQLite with coroutines for database access. Use data classes and extension functions, keep the Bukkit API calls on the main thread."
Questions
Is a Kotlin plugin slower than a Java plugin?
No meaningful difference: both compile to JVM bytecode. The .jar is larger when the standard library is bundled.
Can I mix Java and Kotlin in one plugin?
Yes, Gradle compiles both in the same project, and Kotlin code calls Java classes directly.
Why NoClassDefFoundError for kotlin classes?
The Kotlin standard library is not in the .jar or not loaded. Build with shadowJar and install that .jar, or add the library to libraries in plugin.yml.
Make a Kotlin plugin
This prompt opens ready to send:
“A Paper 1.21.4 plugin in Kotlin called Bounties: /bounty set <player> <amount> using the Vault economy, the killer gets the reward, /bounty top shows the 10 highest, bounties stored in SQLite with coroutines for database access, Bukkit API calls on the main thread, messages in config.yml.”
Generate this with Codexe