Declaring properties in the description block
Properties are defined inside the `description` object of your server-side entity JSON file, right alongside `identifier` and `is_spawnable`. Each property key must include a namespace—never use the `minecraft:` namespace for your own custom properties. An entity can hold up to 32 properties in total.
When an entity spawns, every property initializes to its `default` value. That value can be a literal matching the property's type, or a Molang expression evaluated at spawn time (for example, `Math.random(0, 3)` to pick a random starting integer). If `client_sync` is set to `true`, the server automatically replicates changes to all nearby clients so resource pack animations, particle effects, and render controllers can read the property.
{
"format_version": "1.20.40",
"minecraft:entity": {
"description": {
"identifier": "my_pack:crystal_golem",
"is_spawnable": true,
"is_summonable": true,
"properties": {
"my_pack:charge_level": {
"type": "int",
"range": [0, 10],
"default": 0,
"client_sync": true
},
"my_pack:heat_ratio": {
"type": "float",
"range": [0.0, 1.0],
"default": 0.0,
"client_sync": false
},
"my_pack:is_awakened": {
"type": "bool",
"default": false,
"client_sync": true
},
"my_pack:stance": {
"type": "enum",
"values": ["idle", "guard", "berserk"],
"default": "idle",
"client_sync": true
}
}
},
"components": {
"minecraft:health": {
"value": 40
}
}
}
}Property types and constraints
Every property requires a `type` and a `default` field. Numerical properties (`int` and `float`) also require a `range` array of two numbers specifying the inclusive minimum and maximum values. If an event or script attempts to push a number outside its declared range, the game clamps the value or rejects the change.
For discrete named states, `enum` is usually cleaner than an integer code because the values are self-documenting strings. An `enum` property allows up to 16 distinct strings in its `values` array, and each string must be between 1 and 32 characters long.
| Field | What it does | Notes |
|---|---|---|
| type | Sets the data type: "int", "float", "bool", or "enum". | Required on all properties. |
| default | Initial value assigned when the entity is created. | Accepts a literal value or a Molang string. |
| range | Two-element array [min, max] defining valid bounds. | Required for "int" and "float" types only. |
| values | Array of allowed string states (up to 16 entries). | Required for "enum" type only. |
| client_sync | Boolean controlling whether the client receives updates. | Defaults to false. Set true if RP needs the value. |
Modifying properties inside entity events
On the server side, you mutate properties inside the `events` block of your behavior entity file using the `set_property` action. Unlike component groups, changing a property does not add or remove heavy component trees—it just updates the stored value in place, which is much lighter when you only need to flip a flag or increment a counter.
For numeric properties, `set_property` accepts Molang strings. This lets you read the current value with `query.property()` and perform arithmetic on it within the same event.
{
"format_version": "1.20.40",
"minecraft:entity": {
"description": {
"identifier": "my_pack:crystal_golem",
"is_spawnable": true,
"is_summonable": true,
"properties": {
"my_pack:charge_level": {
"type": "int",
"range": [0, 10],
"default": 0,
"client_sync": true
},
"my_pack:stance": {
"type": "enum",
"values": ["idle", "guard", "berserk"],
"default": "idle",
"client_sync": true
}
}
},
"components": {},
"events": {
"my_pack:add_charge": {
"set_property": {
"my_pack:charge_level": "math.min(10, query.property('my_pack:charge_level') + 1)"
}
},
"my_pack:enter_berserk": {
"set_property": {
"my_pack:stance": "berserk",
"my_pack:charge_level": 0
}
}
}
}
}Filtering by property in AI goals and sensors
To test a property inside entity filters (such as `minecraft:environment_sensor`, `minecraft:interact`, or target filters on AI goals), use the filter test that corresponds to your property's data type: `int_property`, `float_property`, `bool_property`, or `enum_property`.
In each filter, `domain` specifies the full namespaced name of the property you are inspecting, `value` is the target value you are comparing against, and `operator` (such as `==`, `!=`, `<`, `>`, `<=`, `>=`) defines the comparison.
{
"format_version": "1.20.40",
"minecraft:entity": {
"description": {
"identifier": "my_pack:crystal_golem",
"is_spawnable": true,
"is_summonable": true,
"properties": {
"my_pack:charge_level": {
"type": "int",
"range": [0, 10],
"default": 0,
"client_sync": true
}
}
},
"components": {
"minecraft:environment_sensor": {
"triggers": [
{
"filters": {
"test": "int_property",
"domain": "my_pack:charge_level",
"operator": ">=",
"value": 10
},
"event": "my_pack:enter_berserk"
}
]
}
}
}
}Reading properties in Molang and Resource Packs
In Molang expressions—whether in server-side behavior packs or client-side animation controllers and render controllers—you access properties with two functions: `query.property('namespace:name')` (or the alias `q.property()`) and `query.has_property('namespace:name')`.
Remember that a resource pack runs on the client. If you call `q.property('my_pack:stance')` inside a render controller to swap textures, that property must have `"client_sync": true` in the behavior pack definition. If `client_sync` is omitted or `false`, the client has no record of the server's value.
{
"format_version": "1.10.0",
"render_controllers": {
"controller.render.crystal_golem": {
"geometry": "Geometry.default",
"materials": [
{
"*": "Material.default"
}
],
"textures": [
"q.property('my_pack:stance') == 'berserk' ? Texture.berserk : Texture.default"
]
}
}
}Reading and writing from the Script API
If your add-on uses `@minecraft/server`, you can read and mutate entity properties directly on any `Entity` instance without firing JSON events. The `getProperty(identifier)` method returns a `boolean`, `number`, `string`, or `undefined` if the property does not exist on that entity.
To update a value, call `setProperty(identifier, value)`. The value passed must match the declared type and fall within the defined `range` or `values` list. You can also reset a property back to its JSON-defined default by calling `resetProperty(identifier)`, which returns the restored default value.
import { world } from "@minecraft/server";
world.afterEvents.entityHurt.subscribe((event) => {
const entity = event.hurtEntity;
if (entity.typeId !== "my_pack:crystal_golem") return;
const currentCharge = entity.getProperty("my_pack:charge_level");
if (typeof currentCharge === "number" && currentCharge < 10) {
entity.setProperty("my_pack:charge_level", currentCharge + 1);
}
});Which versions this applies to
Stable in format_version 1.20.0 and higher; no experimental toggle is required. Originally introduced behind the Upcoming Creator Features toggle in 1.19.x.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log Error: [Entity][error]-my_pack:crystal_golem | Property 'charge_level' must have a namespace. | You declared a property key without a namespace prefix, or you tried to use the reserved 'minecraft:' prefix. | Prefix every property identifier with your own custom namespace, such as 'my_pack:charge_level', both in the declaration and everywhere you reference it. |
| Client-side animations or render controllers never react when the property changes on the server. | Entity properties default to server-only (`"client_sync": false`) to save network bandwidth, so the client always reads the default or fails the check. | Add `"client_sync": true` to the property's definition inside the behavior pack entity JSON. |
| Content Log Error when loading the world: default value or range is invalid on an int or float property. | Either the `range` array is missing on a numeric property, or the `default` literal falls outside the `[min, max]` bounds. | Ensure every `int` and `float` property has a two-element `range` array (for example, `"range": [0, 10]`) and that `default` is between those two numbers inclusive. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
