This wiki is still very much work in progress! Feel free to suggest changes in #kubejs-and-code Discord channel.

Information (Read this first!)

Website and FAQ: https://kubejs.latvian.dev/

Discord server: https://discord.gg/bPFfH6P

Forge download: https://www.curseforge.com/minecraft/mc-mods/kubejs

Forge source and issue tracker: https://github.com/KubeJS-Mods/KubeJS

Fabric download: https://www.curseforge.com/minecraft/mc-mods/kubejs-fabric

Fabric source and issue tracker: https://github.com/KubeJS-Mods/KubeJS-Fabric

1.12 versions aren't supported, only 1.15 and up!

How to read Variables and Functions
Format Use Example Info

var x = event.variableName

event.variableName = 10

A simple variable, can be read and changed as well
functionName() event.functionName() Function with no parameters, can be called
functionName(int x, String y)

event.functionName(10, 'abc')

Function with parameters with types specified (even if JS doesn't have function types), can be called
variableName var x = event.variableName Variable that can only be read but can't be changed
variableName event.variableName = 10 Variable that can only be changed, but can't be read


Events that get fired during game to control recipes, world, etc.


List of all events

This is a list of all events. It's possible that not all events are listed here, but this list will be updated regularly.

Click on event ID to open it's class and see information, fields and methods.

Type descriptions:

ID Cancellable Type Available on Fabric
init No Startup Yes
postinit No Startup Yes
loaded No Startup Yes
command.registry No Server Startup Yes (Since 3.1)
command.run Yes Server Yes (Since 3.1)
client.init No Client Startup Yes
client.debug_info.left No Client


client.debug_info.right No Client


client.logged_in No Client Yes (Since 3.1)
client.logged_out No Client Yes (Since 3.1)
client.tick No Client Yes
server.load No Server Startup Yes (Since 3.1)
server.unload No Server Yes (Since 3.1)
server.tick No Server Yes
server.datapack.first No Server Startup Yes
server.datapack.last No Server Startup Yes
recipes No Server Startup Yes
world.load No Server Yes
world.unload No Server Yes
world.tick No Server Yes
world.explosion.pre Yes Server Yes (Since 3.1)
world.explosion.post No Server Yes (Since 3.1)
player.logged_in No Server Yes (Since 3.1)
player.logged_out No Server Yes (Since 3.1)
player.tick No Server Yes (Since 3.1)
player.data_from_server. Yes Client Yes (Since 3.1)
player.data_from_client. Yes Server Yes (Since 3.1)
player.chat Yes Server Yes (Since 3.1)
player.advancement No Server Yes (Since 3.1)
player.inventory.opened No Server Yes (Since 3.1)
player.inventory.closed No Server Yes (Since 3.1)
player.inventory.changed No Server Yes (Since 3.1)
player.chest.opened No Server Yes (Since 3.1)
player.chest.closed No Server Yes (Since 3.1)
entity.death Yes Server Yes (Since 3.1)
entity.attack Yes Server Yes (Since 3.1)
entity.drops Yes Server No
entity.check_spawn Yes Server No
entity.spawned Yes Server Yes (Since 3.1)
block.registry No Startup Yes
block.missing_mappings No Server Startup No
block.tags No Server Startup Yes
block.right_click Yes Server Yes
block.left_click Yes Server Yes
block.place Yes Server Yes (Since 3.1)
block.break Yes Server Yes (Since 3.1)
block.drops No Server No
item.registry No Startup Yes
item.missing_mappings No Server Startup No
item.tags No Server Startup Yes
item.right_click Yes Server Yes
item.right_click_empty No Server Yes
item.left_click No Server Yes
item.entity_interact Yes Server Yes (Since 3.1)
item.pickup Yes Server Yes
item.tooltip No Client


item.toss Yes Server


item.crafted No Server Yes (Since 3.1)
item.smelted No Server Yes
fluid.tags No Server Startup Yes
entity_type.tags No Server


worldgen.add No Startup


worldgen.remove No Startup





This event is the most basic event class, parent of all other events.

Parent class


Can be cancelled


Variables and Functions

Name Return Type Info
cancel() void Cancels event. If the event can't be cancelled, it won't do anything.


This event needs cleanup! Using it is not recommended.


This event is fired when a command is executed on server side.

Parent class


Can be cancelled


Variables and Functions

Name Type Info
parseResults ParseResults<CommandSource> Command params 
exception Exception Error, set if something went wrong



This event is fired when a tag collection is loaded, to modify it with script. You can add and remove tags for items, blocks, fluids and entity types.

Parent class


Can be cancelled


Variables and Functions

Name Type Info
type String Tag collection type.
get(String tag) TagWrapper Returns specific tag container which you can use to add or remove objects to. tag parameter can be something like 'forge:ingots/copper'. If tag doesn't exist, it will create a new one.
add(String tag, String[]/Regex ids) TagWrapper Shortcut method for event.get(tag).add(ids).
remove(String tag, String[]/Regex ids) TagWrapper Shortcut method for event.get(tag).remove(ids).
removeAll(String tag) TagWrapper Shortcut method for event.get(tag).removeAll().

TagWrapper class

Variables and Functions

Name Type Info
add(String[]/Regex ids) TagWrapper (itself) Adds an object to this tag. If string starts with # then it will add all objects from the second tag. It can be either single string, regex (/regex/flags) or array of either.
remove(String[]/Regex ids) TagWrapper (itself) Removes an object from tag, works the same as add().
removeAll() TagWrapper (itself) Removes all entries from tag.


// Listen to item tag event
onEvent('item.tags', event => {
  // Get the #forge:cobblestone tag collection and add Diamond Ore to it
  event.add('forge:cobblestone', 'minecraft:diamond_ore')
  // Get the #forge:cobblestone tag collection and remove Mossy Cobblestone from it
  event.remove('forge:cobblestone', 'minecraft:mossy_cobblestone')
  // Get #forge:ingots/copper tag and remove all entries from it

Recipes use item tags, not block or fluid tags, even if items representing those are blocks. Like minecraft:cobblestone even if it's a block, it will still be an item tag for recipes.

block.tags event is for adding tags to block types, it works the same way. You can find existing block tags if you look at a block with F3 mode enabled, on side. These are mostly only used for technical reasons, and like mentioned above, if its for recipes/inventory, you will want to use item.tags even for blocks.




Possible settings you can change for recipes. It's recommended that you put this in it's own server scripts file, like settings.js

// priority: 5

// Enable recipe logging, off by default
settings.logAddedRecipes = true
settings.logRemovedRecipes = true
// Enable skipped recipe logging, off by default
settings.logSkippedRecipes = true
// Enable erroring recipe logging, on by default, recommended to be kept to true
settings.logErroringRecipes = false

Example recipe script:

// kubejs/server_scripts/example.js
// This is just an example script to show off multiple types of recipes and removal methods
// Supports /reload

// Listen to server recipe event
onEvent('recipes', event => {
  // Remove broken recipes from vanilla and other mods
  // This is on by default, so you don't need this line
  //event.removeBrokenRecipes = true

  event.remove({}) // Deletes all recipes
  event.remove({id: 'minecraft:glowstone'}) // Removes data/minecraft/recipes/glowstone.json
  event.remove({input: '#forge:dusts/redstone'}) // Removes all recipes where input is Redstone Dust tag
  event.remove({output: '#minecraft:wool'}) // Removes all recipes where output is Wool tag
  event.remove({mod: 'quartzchests'}) // Remove all recipes from Quartz Chests mod
  event.remove({type: 'minecraft:campfire_cooking'}) // Remove all campfire cooking recipes
  event.remove({output: 'minecraft:cooked_chicken', type: 'minecraft:campfire_cooking'}) // You can combine filters, to create ANDk logic
  // You can use 'mod:id' syntax for 1 sized items. For 2+ you need to use '2x mod:id' or Item.of('mod:id', count) syntax. If you want NBT or chance, 2nd is required

  // Add shaped recipe for 3 Stone from 8 Sponge in chest shape
  // (Shortcut for event.recipes.minecraft.crafting_shaped)
  event.shaped('3x minecraft:stone', [
    'S S',
  ], {
    S: 'minecraft:sponge'

  // Add shapeless recipe for 4 Cobblestone from 1 Stone and 1 Glowstone
  // (Shortcut for event.recipes.minecraft.crafting_shapeless)
  event.shapeless('4x minecraft:cobblestone', ['minecraft:stone', '#forge:dusts/glowstone'])

  // Add Stonecutter recipe for Golden Apple to 4 Apples
  event.stonecutting('4x minecraft:apple', 'minecraft:golden_apple')
  // Add Stonecutter recipe for Golden Apple to 2 Carrots
  event.stonecutting('2x minecraft:carrot', 'minecraft:golden_apple')

  // Add Furnace recipe for Golden Apple to 3 Carrots
  // (Shortcut for event.recipes.minecraft.smelting)
  event.smelting('2x minecraft:carrot', 'minecraft:golden_apple')
  // Similar recipe to above but this time it has a custom, static ID - normally IDs are auto-generated and will change. Useful for Patchouli
  event.smelting('minecraft:golden_apple', 'minecraft:carrot').id('mymodpack:my_recipe_id')

  // Add similar recipes for Blast Furnace, Smoker and Campfire
  event.blasting('3x minecraft:apple', 'minecraft:golden_apple')
  event.smoking('5x minecraft:apple', 'minecraft:golden_apple')
  // You can also add .xp(1.0) at end of any smelting recipe to change given XP
  // Add a smithing recipe that combines 2 items into one (in this case apple and gold ingot into golden apple)
  event.smithing('minecraft:golden_apple', 'minecraft:apple', 'minecraft:gold_ingot')

  // Create a function and use that to make things shorter. You can combine multiple actions
  const multiSmelt = (output, input) => {
    event.smelting(output, input)
    event.blasting(output, input)
  multiSmelt('minecraft:blue_dye', '#forge:gems/lapis')
  multiSmelt('minecraft:black_dye', 'minecraft:ink_sac')
  multiSmelt('minecraft:white_dye', 'minecraft:bone_meal')

  // If you use custom({json}) it will be using vanilla Json/datapack syntax. Must include "type": "mod:recipe_id"!
  // You can add recipe to any recipe handler that uses vanilla recipe system or isn't supported by KubeJS
  // You can copy-paste the json directly, but you can also make more javascript-y by removing quotation marks from keys
  // You can replace {item: 'x', count: 4} in result fields with Item.of('x', 4).toResultJson()
  // You can replace {item: 'x'} / {tag: 'x'} with Ingredient.of('x').toJson() or Ingredient.of('#x').toJson()
  // In this case, add Create's crushing recipe, Oak Sapling to Apple + 50% Carrot
  // Important! Create has integration already, so you don't need to use this. This is just an example for datapack recipes!
    type: 'create:crushing',
    ingredients: [
    results: [
    processingTime: 100
  // Example of using items with NBT in a recipe
  event.shaped('minecraft:book', [
  ], {
    C: '#forge:cobblestone',
    // Item.of('id', {key: value}), it's recommended to use /kubejs hand
    L: Item.of('minecraft:enchanted_book', {StoredEnchantments:[{lvl:1,id:"minecraft:sweeping"}]}),
    // Same principle, but if its an enchantment, there's a helper method
    W: Item.of('minecraft:enchanted_book').enchant('minecraft:respiration', 2),
    G: '#forge:glass'
  // In all shapeless crafting recipes, replace any planks with Gold Nugget in input items
  event.replaceInput({type: 'minecraft:crafting_shapeless'}, '#minecraft:planks', 'minecraft:gold_nugget')
  // In all recipes, replace Stick with Oak Sapling in output items 
  event.replaceOutput({}, 'minecraft:stick', 'minecraft:oak_sapling')

As mentioned before, you can add any recipe from any mod with JSON syntax (see event.custom({})) but these mods are supported as addons with special syntax:



This event isn't complete yet and can only do basic things. Adding dimension-specific features also isn't possible yet, but is planned.

Example script: (kubejs/startup_scripts/worldgen.js)

onEvent('worldgen.add', event => {
  event.addLake(lake => { // Create new lake feature
    lake.block = 'minecraft:diamond_block' // Block ID (Use [] syntax for properties)
    lake.chance = 3 // Spawns every ~3 chunks

  event.addOre(ore => {
    ore.block = 'minecraft:glowstone' // Block ID (Use [] syntax for properties)
    ore.spawnsIn.blacklist = false // Inverts spawn whitelist
    ore.spawnsIn.values = [ // List of valid block IDs or tags that the ore can spawn in
      '#minecraft:base_stone_overworld' // Default behavior - ores spawn in all stone types
    ore.biomes.blacklist = true // Inverts biome whitelist
    ore.biomes.values = [ // Biomes this ore can spawn in
      'minecraft:plains', // Biome ID
      '#nether' // OR #category, see list of categories below
    ore.clusterMinSize = 5 // Min blocks per cluster (currently ignored, will be implemented later, it's always 1)
    ore.clusterMaxSize = 9 // Max blocks per cluster
    ore.clusterCount = 30 // Clusters per chunk
    ore.minHeight = 0 // Min Y ore spawns in
    ore.maxHeight = 64 // Max Y ore spawns in
    ore.squared = true // Adds random value to X and Z between 0 and 16. Recommended to be true
    // ore.chance = 4 // Spawns the ore every ~4 chunks. You usually combine this with clusterCount = 1 for rare ores
  event.addSpawn(spawn => { // Create new entity spawn
    spawn.category = 'creature' // Category, can be one of 'creature', 'monster', 'ambient', 'water_creature' or 'water_ambient'
    spawn.entity = 'minecraft:pig' // Entity ID
    spawn.weight = 10 // Weight
    spawn.minCount = 4 // Min entities per group
    spawn.maxCount = 4 // Max entities per group

All values are optional. All feature types have biomes field like addOre example

Valid biome categories (#category):

This is the order vanilla worldgen happens:

  1. raw_generation
  2. lakes
  3. local_modifications
  4. underground_structures
  5. surface_structures
  6. strongholds
  7. underground_ores
  8. underground_decoration
  9. vegetal_decoration
  10. top_layer_modification

It's possible you may not be able to generate some things in their layer, like ores in dirt, because dirt hasn't spawned yet. So you may have to change the layer by calling ore.worldgenLayer = 'top_layer_modification'. But this is not recommended.



For more information on biomes field, see worldgen.add event page.

onEvent('worldgen.remove', event => {
  event.removeOres(ores => {
    ores.blocks = [ 'minecraft:coal_ore', 'minecraft:iron_ore' ] // Removes coal and iron ore
    ores.biomes.values = [ 'minecraft:plains' ] // Removes it only from plains biomes
  event.removeSpawnsByID(spawns => {
    spawns.entities.values = [
  event.removeSpawnsByCategory(spawns => {
    spawns.biomes.values = [
    spawns.categories.values = [


onEvent('item.tooltip', tooltip => {
  // Add tooltip to all of these items
  tooltip.add(['quark:backpack', 'quark:magnet', 'quark:crate'], 'Added by Quark Oddities')
  // You can also use any ingredient except #tag
  tooltip.add(/refinedstorage:red_/, 'Can be any color')
  // Multiple lines with an array []. You can also escape ' by using other type of quotation marks
  tooltip.add('thermal:latex_bucket', ["Not equivalent to Industrial Foregoing's Latex", 'Line 2'])


Available fields and methods and examples on how to use them



Parent class of all Java objects. 


None (and itself at the same time, don't question it)

Variables and Functions

Name Type Info
toString() String Tag collection type.
equals(Object other) boolean Checks equality with another object.
hashCode() int Hash code of this object. It is used to optimize maps and other things, should never be used for object equality.
class Class Object's type/class.


Class of string objects, such as "abc" (and in JS 'abc' works as well) 



Variables and Functions

Name Type Info
empty boolean Returns if string is empty a.k.a string === ''
toLowerCase() String Returns a copy of this string, but with all characters in upper case
toUpperCase() String Returns a copy of this string, but with all characters in lower case
equalsIgnoseCase(String other) boolean Hash code of this object. It is used to optimize maps and other things, should never be used for object equality.
length() int Number of characters
charAt(int index) char Single character at index

Primitive Types


Primitive types are objects that don't have a real class and don't inherit methods from Object.

All primitive types

Type Java class Info
void Void No type
byte Byte 8 bit decimal number.
short Short 16 bit decimal number.
int Integer 32 bit decimal number, most common decimal type.
long Long 64 bit decimal number.
float Float 32 bit floating point number.
double Double 64 bit floating point number.
char Character Single character in String such as 'a' or '-'.
boolean Boolean Only true and false values. Can be checked in if function without comparing to true, as if (x) or if (!x) instead of if (x == true) or if (x == false).


Constants, classes and functions


Example scripts for various things you can do with KubeJS



Moved to RecipeEventJS!


Custom Blocks

This is a startup script.

onEvent('block.registry', event => {
  event.create('test_block').material('glass').hardness(0.5).displayName('Test Block')

The texture for this block has to be placed in kubejs/assets/kubejs/textures/block/test_block.png.
If you want a custom block model, you can create one in Blockbench and put it in kubejs/assets/kubejs/models/block/test_block.json.


List of available materials - to change break/walk sounds and to change some properties (tool used to mine, etc):




Reflection / Java access

Very limited reflection is possible, but is not recommended. Use it in cases, when mod doesn't have integration.

// side: client
// This is a startup script. This script changes a java variable in ProjectE mod by accessing code directly

// Listen to post-init event, after all mods have loaded
onEvent('postinit', event => {
  // Loads Java class field
  var PECore = java('moze_intel.projecte.PECore')
  // Changes public static boolean DEV_ENVIRONMENT of PECore class to false

Chat Event

This script is peak of human evolution. Whenever someone says "Creeper" in chat, it replies with "Aw man".

events.listen('player.chat', function (event) {
  // Check if message equals creeper, ignoring case
  if (event.message.trim().equalsIgnoreCase('creeper')) {
    // Schedule task in 1 tick, because if you reply immidiently, it will print before player's message
    event.server.scheduleInTicks(1, event.server, function (callback) {
      // Tell everyone Aw man, colored green. Callback data is the server
      callback.data.tell(text.green('Aw man'))

Another example, cancelling the chat event. No need to schedule anything now, because player's message wont be printed,

events.listen('player.chat', function (event) {
  // Check if message equals creeper, ignoring case
  if (event.message.startsWith('!some_command')) {

Network Packets

This script shows how to use network packets:

// Listen to a player event, in this case item right-click
events.listen('item.right_click', function (event) {
  // Check if item was right-clicked on client or server side
  if (event.player.isServer()) {
    // Send data {test: 123} to channel "test_channel_1". Channel ID can be any string, but it's recommended to keep it to snake_case [a-z_0-9].
    // Receiving side will either be client (because its fired from server).
    event.player.sendData('test_channel_1', {
      test: 123
  } else {
    // It's not required to use a different channel ID, but it's recommended.
    // Receiving side will either be server (because its fired from client).
    event.player.sendData('test_channel_2', {
      test: 456

// Listen to event that gets fired when network packet is received from server.
events.listen('player.data_from_server.test_channel_1', function (event) {
  log.info(event.data.get('test').asInt()) // Prints 123

// Listen to event that gets fired when network packet is received from client.
events.listen('player.data_from_client.test_channel_2', function (event) {
  log.info(event.data.get('test').asInt()) // Prints 456

Starting Items

This script adds items on first time player joins, checking gamestages

Requires GameStages mod!

// Listen to player login event
events.listen('player.logged_in', function (event) {
  // Check if player doesn't have "starting_items" gamestage yet
  if (!event.hasGameStage('starting_items')) {
    // Add the gamestage
    // Give some items to player
    event.player.give({ item: 'minecraft:stone_pickaxe', data: 10 })
    event.player.give({ item: 'minecraft:apple', count: 30 })

Custom Items

// Listen to block registry event
events.listen('item.registry', function (event) {
  // The texture for this item has to be placed in kubejs/assets/kubejs/textures/item/test_item.png
  // If you want a custom item model, you can create one in Blockbench and put it in kubejs/assets/kubejs/models/item/test_item.json
  event.create('test_item').displayName('Test Item')

FTB Utilities Rank Promotions

With this script you can have FTB Utilities roles that change over time.

Is for 1.12 only. Requires FTB Utilities.

events.listen('player.tick', function (event) {
  // This check happens every 20 ticks, a.k.a every second
  if (event.player.server && event.player.ticksExisted % 20 === 0) {
    var rank = event.player.data.ftbutilities.rank
    events.post('test_event', {testValue: rank.id})
    var newRank = ftbutilities.getRank(rank.getPermission('promotion.next'))

    if (newRank) {
      var timePlayed = event.player.stats.get('stat.playOneMinute') / 20 // Seconds player has been on server
      var timeRequired = newRank.getPermissionValue('promotion.timer').getInt()

      if (timeRequired > 0 && timePlayed >= timeRequired && rank.addParent(newRank)) {
        if (!events.postCancellable('ftbutilities.rank.promoted.' + newRank.id, {'player': event.player, 'rank': newRank})) {
          event.player.tell('You have been promoted to ' + newRank.getPermission('promotion.name') + '!')

// When player gets promoted to 'trusted' rank, give them gold ingot (uncomment the line)
events.listen('ftbutilities.rank.promoted.trusted', function (event) {
  // event.data.player.give('minecraft:gold_ingot')

3 example roles in ranks.txt:

power: 1
default_player_rank: true
promotion.name: Player
promotion.next: newcomer
promotion.timer: 5
command.ftbutilities.rtp: false
command.ftbutilities.home: false

power: 5
promotion.name: Newcomer
promotion.next: regular
promotion.timer: 15
ftbutilities.chat.name_format: <&aNewcomer &r{name}>
command.ftbutilities.rtp: true

power: 10
promotion.name: Regular
promotion.next: trusted
promotion.timer: 30
ftbutilities.chat.name_format: <&9Regular &r{name}>
command.ftbutilities.home: true

After 5 seconds of play time, player will be promoted to newcomer.
After 15 seconds (or 10 since previous role) they will be promoted to regular.
After 30 seconds (or 15 since previous role) they will be promoted to trusted, etc.



This script removes all items from world every 30 minutes. Only works in 1.12.

// Create item whitelist filter that won't be deleted with clearlag
var whitelist = Ingredient.matchAny([
  'minecraft:diamond', // Adds diamond to whitelist
  '@tinkersconstruct', // Adds all items from tinkerscontruct to whitelist

// Create variable for last clearlag result
var lastClearLagResult = Utils.newList()
// Create variable for total number of items
var lastTotalClearLagResult = Utils.newCountingMap()

// Create new function that clears lag
var clearLag = server => {
  // Get a list of all entities on server with filter that only returns items
  var itemList = server.getEntities('@e[type=item]')
  // Create new local map for item counters
  var lastResult = Utils.newCountingMap()
  // Clear old result lists
  // Iterate over each entity in itemList and add item counters
  itemList.forEach(entity => {
    if (!whitelist.test(entity.item)) {
      // Get the name of item
      var key = entity.item.name
      // Add to entity count
      lastResult.add(key, 1)
      // Add to total item count
      lastTotalClearLagResult.add(key, entity.item.count)
      // Kill the item entity

  // Update and sort last result list

  // Tell everyone how many items will be removed
    ' Removed ',
    ' items. ',
    Text.yellow('Click here').click('command:/clearlagresults'),
    ' for results.'

// Listen for server load event
events.listen('server.load', event => {
  // Log message in console
  event.server.tell([ Text.lightPurple('[ClearLag]'), ' Timer started, clearing lag in 30 minutes!' ])
  // Schedule new task in 30 minutes
  event.server.schedule(MINUTE * 30, event.server, callback => {
    // Tell everyone on server that items will be removed
    callback.data.tell([ Text.lightPurple('[ClearLag]'), ' Removing all items on ground in one minute!' ])
    // Schedule a subtask that will clear items in one minute
    callback.data.schedule(MINUTE, callback.data, callback2 => {
    // Re-schedule this task for another 30 minutes (endless loop)

// Doesnt work in 1.16+!
// Register commands
events.listen('command.registry', event => {
  // Register new OP command /clearlag, that instantly runs clearlag
    .execute(function (sender, args) {

  // Register new non-OP command /clearlagresults, that displays stats of all removed items from previous /clearlag
    .execute((sender, args) => {
      sender.tell([ Text.lightPurple('[ClearLag]'), ' Last clearlag results:' ])

      lastClearLagResult.forEach(entry => {
        var total = lastTotalClearLagResult.get(entry.key)

        if (entry.value == total) {
          sender.tell([ Text.gold(entry.key), ': ', Text.red(entry.value) ])
        } else {
          sender.tell([ Text.gold(entry.key), ': ', Text.red(entry.value), ' entities, ', Text.red(total), ' total' ])

Scheduled Server Events

At server load, you can schedule anything to happen at later time. Within callback handler you can also call callback.reschedule() to repeat this event after initial timer or callback.reschedule(newTime) to change it.

Whatever you pass as 2nd argument will be returned in callback as data.

The example script restarts server after 2 hours but notifies players 5 minutes before that.

events.listen('server.load', function (event) {
  event.server.schedule(115 * MINUTE, event.server, function (callback) {
    callback.data.tell('Server restarting in 5 minutes!')
  event.server.schedule(120 * MINUTE, event.server, function (callback) {

FTB Quests Integration

events.listen('ftbquests.custom_task.75381f79', function (event) {
  log.info('Custom task!')
  event.checkTimer = 20
  event.check = function (task, player) {
    if (player.world.daytime && player.world.raining) {

events.listen('ftbquests.custom_reward.e4f76908', function (event) {
  log.info('Custom reward!')

events.listen('ftbquests.completed.abc', function (event) {
  event.notifiedPlayers.tell('Custom task completed!')

events.listen('ftbquests.completed.ding', function (event) {

events.listen('entity.death', function (event) {
  && event.source.actual
  && event.source.actual.player
  && event.source.actual.mainHandItem.id.equals('minecraft:wooden_sword')
  && event.entity.type.equals('minecraft:zombie')) {
    event.source.actual.data.ftbquests.addProgress('12345678', 1)

JEI Integration


onEvent('jei.subtypes', event => {
  event.userNBTKey('example:item', 'type')

Hide Items & Fluids

onEvent('jei.hide.items', event => {

onEvent('jei.hide.fluids', event => {

Add Items & Fluids

onEvent('jei.add.items', event => {
  event.add(Item.of('example:item', {test: 123}))

onEvent('jei.add.fluids', event => {

Add Information

onEvent('jei.information', event => {
  event.add('example:ingredient', ['Line 1', 'Line 2'])

REI Integration (Fabric Only)

Hide Items

onEvent('rei.hide.items', event => {

Add Items

onEvent('rei.add.items', event => {
  event.add(item.of('example:item').nbt({test: 123}))

Add Information

onEvent('rei.information', event => {
  event.add('example:ingredient', 'Title', ['Line 1', 'Line 2'])

Changing Window Title and Icon

Yes, you can do that with KubeJS too.

To change title, all you have to do is change title in kubejs/config/client.properties.

To change icon, you create a kubejs/config/packicon.png image in standard Minecraft texture size preferably (64x64, 128x128, 256x256, that kind of size).

The image has to be saved as 32-bit PNG, not Auto-detect/24-bit, otherwise you will get a JVM crash!

Here's how to do that in PaintNET:

Example result:

Currently incompatible with Fancy Menu!



You can also always look at existing modpack using KubeJS UI to see how they do it

events.listen('ui.main_menu', event => {
  event.replace(ui => {
    ui.tilingBackground('kubejsui:textures/example_background.png', 256)
    ui.button(b => {
      b.name = 'Test'
      b.x = 10
      b.y = 10
      b.action = 'minecraft:singleplayer'
    ui.button(b => {
      b.name = 'Test but in bottom right corner'
      b.x = ui.width - b.width - 10
      b.y = ui.height - b.height - 10
      b.action = 'https://feed-the-beast.com/'
    ui.label(l => {
      l.name = text.yellow('FTB Stranded')
      l.x = 2
      l.y = ui.height - 12
      l.action = 'https://feed-the-beast.com/'

    ui.image(i => {
      i.x = (ui.width - 40) / 2
      i.y = (ui.height - 30) / 2
      i.width = 40
      i.height = 30
      i.action = 'https://feed-the-beast.com/'
    ui.label(l => {
      l.name = text.aqua('Large label')
      l.x = 100
      l.y = ui.height - 20
      l.height = 15
      l.shadow = true

Loot Table Modification (WIP)

onEvent('block.loot_tables', event => {
  event.addSimpleBlock('minecraft:dirt', 'minecraft:red_sand')
onEvent('block.loot_tables', event => {
  event.addSimpleBlock('minecraft:dirt') // To drop itself (fix broken blocks)
  event.addSimpleBlock(/minecraft:.*_ore/, 'minecraft:red_sand') // To drop a different item
onEvent('block.loot_tables', event => {
  event.build('minecraft:dirt', table => { // Build loot table manually
    table.pool(pool => {
      pool.rolls = 1 // Use one of these 3 - fixed, uniform, binominal
      // pool.setUniformRolls(4, 6)
      // pool.setBinomialRolls(4, 0.3)
      // pool.addCondition({json condition, see vanilla wiki})
      // pool.addEntry({json entry, see vanilla wiki for non-items})

Example from FTB Stranded: (adds 1-3 leaves dropped from all Leaves blocks, 4-8 logs from all log and wood blocks and 4-8 stone from Stone, Cobblestone, Andesite, Diorite and Granite)

onEvent('block.loot_tables', event => {
	event.addBlock(/minecraft:.*_leaves/, table => {
		table.pool(pool => {
			pool.setUniformRolls(1, 3)
			pool.addEntry({type: 'minecraft:item', name: 'ftbstranded:leaf'})

	event.addBlock(/minecraft:.*_(log|wood)/, table => {
		table.pool(pool => {
			pool.setUniformRolls(4, 8)
			pool.addEntry({type: 'minecraft:item', name: 'ftbstranded:wood'})

	], table => {
		table.pool(pool => {
			pool.setUniformRolls(4, 8)
			pool.addEntry({type: 'minecraft:item', name: 'ftbstranded:stone'})