Wiki 9Minecraft Wiki 9Minecraft

What is the Script API?

Overview of Minecraft Bedrock's JavaScript runtime, modules, and execution rules.

5Sections
2Code examples
3Known errors

The Script API is a built-in JavaScript and TypeScript runtime inside Minecraft Bedrock Edition that lets behavior packs run imperative code alongside data-driven JSON. You reach for it when Molang expressions, entity events, and `/execute` command chains become too rigid or slow—such as building custom UI menus, managing complex minigame state, or writing custom block and item mechanics.

How the Runtime Works

When a world loads with a script-enabled behavior pack, Bedrock spins up an isolated QuickJS engine on the server side (which in singleplayer or local host is still your device, running on the internal server thread). Your scripts do not have access to Node.js APIs, browser DOM objects, or the local filesystem. Instead, the game exposes specific host modules that you import using standard ES6 module syntax.

The engine runs on the game's 20-ticks-per-second loop. Script execution is strictly bounded by a watchdog timer: if your code enters an infinite loop or performs a massive synchronous calculation that stalls a tick for too long, the watchdog terminates your pack's script execution and prints a spike warning to the Content Log to prevent the server from crashing.

This modern system completely replaces the deprecated `minecraft:client_scripts` and `minecraft:server_scripts` architecture from older 1.13–1.16 betas, as well as the removed Holiday Creator Features `run_command` event bridges in items and blocks. All imperative logic for modern blocks and items now routes through the Script API's custom component registry.

Core Script Modules

Rather than bundling every API into a single massive object, Bedrock splits functionality across scoped npm-style module names. You declare which modules and versions your pack needs inside your behavior pack's `manifest.json`, and then import them at the top of your JavaScript files.

Some modules have graduated to stable versioning, meaning packs written against them will continue to work across future game updates without needing world experimental toggles. Others remain in `-beta` status and require the Beta APIs toggle to be enabled on the world.

Module NameWhat it doesNotes
@minecraft/serverCore world, dimension, entity, block, item, scoreboard, and system tick APIs.Has stable 1.x releases plus an active -beta track for newest features.
@minecraft/server-uiCreates native pop-up dialogs: ActionFormData, ModalFormData, and MessageFormData.Has stable 1.x releases. Essential for server menus and settings screens.
@minecraft/server-gametestProvides automated testing helpers and simulated players for verifying add-on mechanics.Remains on the -beta track; requires the Beta APIs experimental toggle.
@minecraft/server-netAllows outbound HTTP requests to external web servers.Available only on Bedrock Dedicated Server (BDS) with explicit server configuration.
@minecraft/server-adminManages server variables and administrative configuration.Available only on Bedrock Dedicated Server (BDS).

Enabling Scripts in manifest.json

To tell Minecraft that your behavior pack contains scripts, you must add two things to your `BP/manifest.json`: a `script` module inside the `modules` array, and a `dependencies` array listing the exact `@minecraft/*` packages your code imports.

The `entry` field inside the script module points to the main `.js` file inside your behavior pack's `scripts/` folder. Even if you write your project in TypeScript, you must compile your `.ts` files down to `.js` before the game can read them, and `entry` must point to the compiled JavaScript output.

BP/manifest.json
{
  "format_version": 2,
  "header": {
    "name": "Script API Starter",
    "description": "Basic behavior pack configured for stable Script API",
    "uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
    "version": [1, 0, 0],
    "min_engine_version": [1, 21, 0]
  },
  "modules": [
    {
      "type": "data",
      "uuid": "b2c3d4e5-f6a7-4b5c-9d0e-1f2a3b4c5d6e",
      "version": [1, 0, 0]
    },
    {
      "type": "script",
      "language": "javascript",
      "uuid": "c3d4e5f6-a7b8-4c5d-0e1f-2a3b4c5d6e7f",
      "version": [1, 0, 0],
      "entry": "scripts/main.js"
    }
  ],
  "dependencies": [
    {
      "module_name": "@minecraft/server",
      "version": "1.11.0"
    },
    {
      "module_name": "@minecraft/server-ui",
      "version": "1.2.0"
    }
  ]
}

Events, Ticks, and Read-Only Mode

The most important architectural rule in `@minecraft/server` is the split between `beforeEvents` and `afterEvents`. When something happens in the world—like a player breaking a block or sending a chat message—the engine fires `beforeEvents` first. During a `before` event callback, the event has not actually happened yet, which lets you set `event.cancel = true` to stop it.

However, while a `before` event is running, the world is in read-only mode (often called restricted execution). You cannot spawn entities, modify blocks, give items, or change properties directly inside a `before` event callback. If you need to cancel an action and also modify the world in response, you cancel the `before` event and wrap your world-modifying code inside `system.run(() => { ... })`, which defers the execution until later in the tick when the world is writable again.

By contrast, `afterEvents` fire once the action is already committed to the world. You cannot cancel an `after` event, but you have full read and write access to modify blocks, entities, and inventories immediately.

BP/scripts/main.js
import { world, system } from "@minecraft/server";

// Listen to an afterEvent: world modification is allowed directly here
world.afterEvents.playerSpawn.subscribe((event) => {
  if (event.initialSpawn) {
    event.player.sendMessage("Welcome to the world!");
  }
});

// Listen to a beforeEvent: world is in read-only mode during this callback
world.beforeEvents.playerBreakBlock.subscribe((event) => {
  if (event.block.typeId === "minecraft:diamond_ore") {
    // Cancelling is allowed in beforeEvents
    event.cancel = true;

    // Defer world/entity changes to normal tick execution using system.run
    system.run(() => {
      event.player.sendMessage("You cannot mine diamond ore directly!");
      event.dimension.spawnParticle("minecraft:villager_angry", event.block.location);
    });
  }
});

// Run a repeating task every 20 ticks (1 second)
system.runInterval(() => {
  for (const player of world.getAllPlayers()) {
    if (player.isInWater) {
      player.onScreenDisplay.setActionBar("Swimming...");
    }
  }
}, 20);

When to Use Script API vs JSON and Molang

Because the Script API is so flexible, new creators often try to build everything in JavaScript. However, native JSON components and Molang expressions run in compiled C++ inside the engine and are significantly faster when evaluated across hundreds of entities every frame.

As a rule of thumb, use JSON components and Molang for high-frequency, per-entity tasks like AI goals (`minecraft:behavior.*`), animation controllers, particle math, and basic block placement rules. Reach for the Script API when you need event-driven glue logic: responding to a custom block interaction (`minecraft:custom_components`), storing complex structured data using dynamic properties, creating multi-step forms with `@minecraft/server-ui`, or raycasting along a player's view vector.

Which versions this applies to

Applies to Minecraft Bedrock 1.19.50 and newer, with major stable expansions across 1.20 and 1.21. Stable module versions (such as "1.11.0" or "1.2.0") work in any standard world with no experimental toggles required. Using "-beta" module versions (such as "1.12.0-beta") requires enabling the "Beta APIs" experimental toggle in the world settings.

When it does not work

What you seeWhyThe fix
Content Log error: "Module [@minecraft/server] with version [1.x.0-beta] is not a valid dependency" or scripts silently fail to load.You declared a `-beta` version in `manifest.json` without turning on the "Beta APIs" experimental toggle on the world, or you typed a `-beta` version string that was replaced when Minecraft updated to a new minor release.Switch the version in `manifest.json` to a stable release number (for example, `"1.11.0"`) so your pack neither needs the Beta APIs toggle nor breaks when the game updates.
Runtime error in Content Log: "ReferenceError: Native function ... does not have required privileges."You called a method that modifies the world (such as `entity.addEffect`, `block.setType`, or `player.runCommand`) directly inside a `beforeEvents` callback or during initial script file evaluation while the engine is in read-only mode.Wrap the modifying code inside `system.run(() => { /* your code here */ });` so it runs during the normal writable phase of the game tick, or switch your listener from `beforeEvents` to `afterEvents` if you do not need to cancel the event.
Content Log error: "Script entry [scripts/main.js] not found."The path in `manifest.json` under `entry` does not match the actual file inside your behavior pack, or you wrote TypeScript (`.ts`) files and forgot to compile them into JavaScript (`.js`) inside the `BP/scripts/` folder.Verify that `BP/scripts/main.js` physically exists in your behavior pack directory and that `entry` in `manifest.json` is written relative to the pack root, starting with `scripts/`.

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.