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

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

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

Fabric download: https://www.curseforge.com/minecraft/mc-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
init No Startup
postinit No Startup
loaded No Startup
command.registry No Server
command.run Yes Server
client.init No Client
client.debug_info.left No Client
client.debug_info.right No Client
client.logged_in No Client
client.logged_out No Client
client.tick No Client
server.load No Server
server.unload No Server
server.tick No Server
server.datapack.first No Server
server.datapack.last No Server
recipes No Server
world.load No Server
world.unload No Server
world.tick No Server
world.explosion.pre Yes Server
world.explosion.post No Server
player.logged_in No Server
player.logged_out No Server
player.tick No Server
player.data_from_server. Yes Client
player.data_from_client. Yes Server
player.chat Yes Server
player.advancement No Server
player.inventory.opened No Server
player.inventory.closed No Server
player.inventory.changed No Server
player.chest.opened No Server
player.chest.closed No Server
entity.death Yes Server
entity.attack Yes Server
entity.drops Yes Server
entity.check_spawn Yes Server
entity.spawned Yes Server
block.registry No Startup
block.missing_mappings No Server
block.tags No Server
block.right_click Yes Server
block.left_click Yes Server
block.place Yes Server
block.break Yes Server
block.drops No Server
item.registry No Startup
item.missing_mappings No Server
item.tags No Server
item.right_click Yes Server
item.right_click_empty No Server
item.left_click No Server
item.entity_interact Yes Server
item.modification No Startup
item.pickup Yes Server
item.tooltip No Client
item.toss Yes Server
item.crafted No Server
item.smelted No Server
fluid.registry No Startup
fluid.tags No Server
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().
removeAllTagsFrom(String[] ids) void Removes all tags from object

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('tags.items', 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
  // Required for FTB Quests to check item NBT
  event.add('itemfilters:check_nbt', 'some_item:that_has_nbt_types')
  // You can create new tags the same way you add to existing, just give it a name
  event.add('forge:completely_new_tag', 'minecraft:clay_ball')
  // Removes all tags from this entry

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.

tags.blocks 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 tags.items even for blocks.




The most basic script to add a single recipe:

onEvent('recipes', event => {
  event.shaped('3x minecraft:stone', [
    'S S',
  ], {
    S: 'minecraft:sponge',
    A: 'minecraft:apple'

The most basic script to remove a recipe:

onEvent('recipes', event => {
  event.remove({output: 'minecraft:stick'})

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({}) // Removes all recipes (nuke option, usually not recommended)
  event.remove({output: 'minecraft:stone_pickaxe'}) // Removes all recipes where output is stone pickaxe
  event.remove({output: '#minecraft:wool'}) // Removes all recipes where output is Wool tag
  event.remove({input: '#forge:dusts/redstone'}) // Removes all recipes where input is Redstone Dust 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({id: 'minecraft:glowstone'}) // Removes recipe by ID. in this case, data/minecraft/recipes/glowstone.json
  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)
  // If you want to use Extended Crafting, replace event.shapeless with event.recipes.extendedcrafting.shapeless_table
  event.shaped('3x minecraft:stone', [
    'S S',
  ], {
    S: 'minecraft:sponge',
    A: 'minecraft:apple'

  // Add shapeless recipe for 4 Cobblestone from 1 Stone and 1 Glowstone
  // (Shortcut for event.recipes.minecraft.crafting_shapeless)
  // If you want to use Extended Crafting, replace event.shapeless with event.recipes.extendedcrafting.shaped_table
  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')
  event.campfireCooking('8x 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
  let multiSmelt = (output, input, includeBlasting) => {
    event.smelting(output, input)
    if (includeBlasting) {
      event.blasting(output, input)
  multiSmelt('minecraft:blue_dye', '#forge:gems/lapis', true)
  multiSmelt('minecraft:black_dye', 'minecraft:ink_sac', true)
  multiSmelt('minecraft:white_dye', 'minecraft:bone_meal', false)

  // 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')

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

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:

Poorly documented things below!

You can transform ingredients in shaped and shapeless recipes by adding these functions at end of it:

IngredientFilter can be either


onEvent('recipes', event => {
  	event.shapeless('9x minecraft:melon_slice', [ // Craft 9 watermelon slices
		Item.of('minecraft:diamond_sword').ignoreNBT(), // Diamond sword that ignores damage
		'minecraft:minecraft:melon' // Watermelon block
	]).damageItem(Item.of('minecraft:diamond_sword').ignoreNBT()) // Damage the sword (also has to ignore damage or only 0 damage will work)
    // Craft example block from 2 diamond swords and 2 dirt. After crafting first diamond sword is damaged (index 0) and 2nd sword is kept without changes.
	event.shaped('kubejs:example_block', [
		'SD ',
		'D S'
	], {
		S: Item.of('minecraft:diamond_sword').ignoreNBT(),
		D: 'minecraft:dirt'

    // Craft example block from 2 diamond swords and 2 stones. After crafting, diamond sword is replaced with stone sword
	event.shapeless('kubejs:example_block', [
	]).replaceIngredient('minecraft:diamond_sword', 'minecraft:stone_sword')

    // Craft clay from sand, bone meal, dirt and water bottle. After crafting, glass bottle is left in place of water bottle
	event.shapeless('minecraft:clay', [
		Item.of('minecraft:potion', {Potion: "minecraft:water"})
	]).replaceIngredient({item: Item.of('minecraft:potion', {Potion: "minecraft:water"})}, 'minecraft:glass_bottle')


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.

If you want to remove things, see this event.



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 = [

If something isn't removing, you may try to remove it "manually" by first printing all features (this will spam your console a lot, I suggest reading logs/kubejs/startup.txt) and then removing them by ID where possible.

onEvent('worldgen.remove', event => {
  // May be one of the decoration types/levels described in worldgen.add docs
  // But ores are *most likely* to be generated in this one
onEvent('worldgen.remove', event => {
  event.removeFeatureById('underground_ores', 'mekanism:ore_copper')


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'])
  tooltip.addAdvanced('thermal:latex_bucket', (item, advanced, text) => {
    text.add(0, Text.of('Hello')) // Adds text in first line, replacing title. If you want 2nd line, the index must be 1


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



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

onEvent('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

Custom Items

This is a startup_scripts/ event

// Listen to item registry event
onEvent('item.registry', 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', item => {
    item.displayName('Test Item')
  // or, in KubeJS 3 for MC 1.16:
  event.create('test_item').displayName('Test Item')

Other methods item builder supports: [you can chain these methods after displayName()]

Valid item types:

Valid item tiers:

Valid group/creative tab IDs:

Creating custom tool and armor tiers

All values are optional and by default are based on iron tier

onEvent('item.registry.tool_tiers', event => {
  event.add('tier_id', tier => {
    tier.uses = 250
    tier.speed = 6.0
    tier.attackDamageBonus = 2.0
    tier.level = 2
    tier.enchantmentValue = 14
    tier.repairIngredient = '#forge:ingots/iron'
onEvent('item.registry.armor_tiers', event => {
  // Slot indicies are [FEET, LEGS, BODY, HEAD]
  event.add('tier_id', tier => {
    tier.durabilityMultiplier = 15 // Each slot will be multiplied with [13, 15, 16, 11]
    tier.slotProtections = [2, 5, 6, 2]
    tier.enchantmentValue = 9
    tier.equipSound = 'minecraft:item.armor.equip_iron'
    tier.repairIngredient = '#forge:ingots/iron'
    tier.toughness = 0.0 // diamond has 2.0, netherite 3.0
    tier.knockbackResistance = 0.0

Custom Blocks

This is a startup script.

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

// or, in KubeJS 3 for MC 1.16:
onEvent('block.registry', event => {
       .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):





Other methods block builder supports: [you can chain these methods after displayName()]

Event callbacks:



Loot Table Modification

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.addBlock('minecraft:dirt', table => { // Build loot table manually
    table.addPool(pool => {
      pool.rolls = 1 // fixed
      // pool.rolls = [4, 6] // or {min: 4, max: 6} // uniform
      // pool.rolls = {n: 4, p: 0.3} // binominal
      pool.addItem('minecraft:dirt', 40) // 40 = weight
      pool.addItem('minecraft:dirt', 40, [4, 8]) // [4-8] = count modifier, uses same syntax as rolls
      // pool.addCondition({json condition, see vanilla wiki})
      // pool.addEntry({json entry, see vanilla wiki for non-items})

Example from Factorial: (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.addPool(pool => {
			pool.addItem('factorial:leaf', 1, [1, 3])

	event.addBlock(/minecraft:.*_(log|wood)/, table => {
		table.addPool(pool => {
			pool.addItem('factorial:wood', 1, [4, 8])

	], table => {
		table.addPool(pool => {
			pool.rolls = [4, 8] // Roll the pool instead of individual items
			pool.addItem('factorial:stone', 1)

You can also modify existing loot tables to add items to them:

onEvent('block.loot_tables', event => {
  // all dirt blocks have a 50% chance to drop an enchanted diamond sword named "test"
  event.modifyBlock(/^minecraft:.*dirt/, table => {
    table.addPool(pool => {
      pool.addItem('minecraft:diamond_sword').randomChance(0.5).enchantWithLevels(1, true).name(Text.of('Test').blue())

Other loot table types work too:

onEvent('entity.loot_tables', event => {
  // Override zombie loot table that will drop 5 of either carrot (25% chance) or apple (75% chance)
  event.addEntity('minecraft:zombie', table => {
    table.addPool(pool => {
      pool.rolls = 5
      pool.addItem('minecraft:carrot', 1)
      pool.addItem('minecraft:apple', 3)
  event.modifyEntity('minecraft:pig', table => {
    table.addPool(pool => {
      // Modify pig loot table to *also* drop dirt on top of its regular drops

Supported table types:

Event ID Override method name Modify method name
generic.loot_tables addGeneric modify
block.loot_tables addBlock modifyBlock
entity.loot_tables addEntity modifyEntity
gift.loot_tables addGift modify
fishing.loot_tables addFishing modify
chest.loot_tables addChest modify



Item Modification

item.modification event is a startup script event that allows you to change properties of existing items

onEvent('item.modification', event => {
  event.modify('minecraft:ender_pearl', item => {
    item.maxStackSize = 64
    item.fireResistant = true

All available properties:


Block Modification

block.modification event is a startup script event that allows you to change properties of existing blocks

onEvent('block.modification', event => {
  event.modify('minecraft:stone', block => {
    block.destroySpeed = 0.1
    block.hasCollision = false

All available properties:


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 (WIP)

Note: REI integration only works on Fabric in 1.16. In 1.18+, it works on both Forge and Fabric!

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'])

FTB Quests Integration

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

onEvent('ftbquests.custom_reward.e4f76908', event => {
  log.info('Custom reward!')

// specific object completion
onEvent('ftbquests.completed.d4f36905', event => {
  if (event.player) {
    event.notifiedPlayers.tell(Text.of(`${event.player.name} completed... something!`).green())

// generic 'quest' object completion. Note: There isnt actually a way to get reliable title on server side, so dont use event.object.title
onEvent('ftbquests.completed', event => {
  if (event.player && event.object.objectType.id === 'quest') {
    event.notifiedPlayers.tell(Text.of(`${event.player.name} completed a quest!`).blue())

// object with tag 'ding' completion
onEvent('ftbquests.completed.ding', event => {

onEvent('entity.death', event => {
  && event.source.actual
  && event.source.actual.player
  && event.source.actual.mainHandItem.id === 'minecraft:wooden_sword'
  && event.entity.type === 'minecraft:zombie') {
    event.source.actual.data.ftbquests.addProgress('12345678', 1)

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

Painter API


Painter API allows you to draw things on the screen, both from server and directly from client. This can allow you to create widgets from server side or effects on screen or in world from client side.

Currently it doesn't support any input, but in future, in-game menus or even GUIs similar to Source engine ones will be supported.

Paintable objects are created from NBT/Json objects and all have an id. If id isn't provided, a random one will be generated. Objects x and z are absolute positions based on screen, but you can align elements in one of the corners of screen. You can bulk add multiple objects in one json object. All properties are optional, but obviously some you should almost always override like size and position for rectangles.

paint({...}) is based on upsert principle - if object doesn't exist it will create it (if the object also contains valid type), otherwise, update existing:

You can bulk update/create multiple things in same object:

You can remove object with remove: true, bulk remove multiple objects or remove all objects:

These methods have command alternatives:

If the object is re-occuring, it's recommended to create objects at login with all of its static properties and visible: false, then update it later to unhide it. Painter objects will be cleared when players leave world/server, if its persistent, then it must be re-added at login every time.

Currently available objects

Underlined objects are not something you can create


(available for all objects)

Screen Object

(available for all screen / 2D objects)

World Object


Available Unit variables


onEvent('player.logged_in', event => {
		example_rectangle: {
			type: 'rectangle',
			x: 10,
			y: 10,
			w: 50,
			h: 20,
			color: '#00FF00',
			draw: 'always'
		last_message: {
			type: 'text',
			text: 'No last message',
			scale: 1.5,
			x: -4,
			y: -4,
			alignX: 'right',
			alignY: 'bottom',
			draw: 'always'

onEvent('player.chat', event => {
	// Updates example_rectangle x value and last_message text value to last message + contents from event
	event.player.paint({example_rectangle: {x: '(sin((time() * 1.1)) * (($screenW - 32) / 2))', w: 32, h: 32, alignX: 'center', texture: 'kubejs:textures/item/diamond_ore.png'}})
	event.player.paint({last_message: {text: `Last message: ${event.message}`}})
	// Bulk update, this is the same code as 2 lines above, you can use whichever you like better
	// event.player.paint({example_rectangle: {x: 120}, last_message: {text: `Last message: ${event.message}`}})
	event.player.paint({lava: {type: 'atlas_texture', texture: 'minecraft:block/lava_flow'}})