How Block Orientation Works in Bedrock
In older versions of the Bedrock add-on format, rotating a block required declaring custom block states, writing an `on_player_placing` event, and running Molang queries like `query.cardinal_facing_2d` to set the state on placement. Since those legacy event triggers were removed in format versions 1.20.20 and above, orientation is now handled almost entirely through Block Traits.
A block trait is a built-in preset added to the `description` object of your block JSON. When you declare a placement trait, the game engine automatically injects the required directional states into your block and sets their values at the exact moment a player places the block in the world.
Important: traits only manage the data state on the block. They do not automatically spin your block's model or textures. To actually see the block turn in-game, you must read the trait's state inside the `permutations` array and apply a rotation to the `minecraft:transformation` component.
The Placement Direction Trait
The `minecraft:placement_direction` trait records the direction the player is looking when they place the block. It sits inside a `traits` object inside your block's `description`. You activate the specific states you need by listing them in the `enabled_states` array.
You can enable one or both of the available states, depending on whether your block should only spin horizontally (like a carved pumpkin or lectern) or also tilt up and down (like a dispenser or observer). You can also define a `y_rotation_offset` on the trait to shift the horizontal angle by 90-degree increments—for example, setting it to `180.0` makes the block's front face point back toward the player instead of away from them.
| Enabled State | Possible Values | Typical Use |
|---|---|---|
| minecraft:cardinal_direction | "north", "south", "east", "west" | Horizontal-only blocks such as furnaces, chests, and chairs. |
| minecraft:facing_direction | "down", "up", "north", "south", "east", "west" | Full 6-way directional blocks that respond to looking up or down, such as pistons and observers. |
The Placement Position Trait
While `minecraft:placement_direction` looks at the player's camera angle, the `minecraft:placement_position` trait looks at the exact point on an existing block that the player clicked with their crosshair.
This trait is used when a block should attach to a surface or snap to the top or bottom half of a cell, regardless of which compass direction the player is facing.
| Enabled State | Possible Values | Typical Use |
|---|---|---|
| minecraft:block_face | "down", "up", "north", "south", "east", "west" | Blocks that attach to the clicked surface, such as buttons, torches, or logs. |
| minecraft:vertical_half | "bottom", "top" | Slabs, trapdoors, or stairs that place in the upper half when clicking the top half of a side face. |
Rotating the Model with Permutations
Once a trait is enabled, you match its state values in `permutations` using the `q.block_state()` Molang query. Inside each matching permutation, you override the `minecraft:transformation` component and set its `rotation` array `[x, y, z]`.
Transformation rotations are strictly limited to 90-degree increments (`0`, `90`, `180`, `270`, or `-90`). In Blockbench, the front of a block faces North (`-Z`) by default. Rotating around the Y axis spins the block across the compass directions: `0` is North, `180` is South, `90` is West, and `270` (or `-90`) is East.
Remember that `minecraft:transformation` rotates the entire block geometry and its UV mapping together. If you are using a standard unit cube (`minecraft:geometry.full_block`) without custom geometry, rotating the transformation will rotate the textures on the top and bottom faces as well.
{
"format_version": "1.20.80",
"minecraft:block": {
"description": {
"identifier": "wiki:custom_oven",
"menu_category": {
"category": "items"
},
"traits": {
"minecraft:placement_direction": {
"enabled_states": [
"minecraft:cardinal_direction"
],
"y_rotation_offset": 180.0
}
}
},
"components": {
"minecraft:geometry": "geometry.custom_oven",
"minecraft:material_instances": {
"*": {
"texture": "custom_oven_side",
"render_method": "opaque"
},
"north": {
"texture": "custom_oven_front",
"render_method": "opaque"
}
}
},
"permutations": [
{
"condition": "q.block_state('minecraft:cardinal_direction') == 'north'",
"components": {
"minecraft:transformation": {
"rotation": [0, 0, 0]
}
}
},
{
"condition": "q.block_state('minecraft:cardinal_direction') == 'south'",
"components": {
"minecraft:transformation": {
"rotation": [0, 180, 0]
}
}
},
{
"condition": "q.block_state('minecraft:cardinal_direction') == 'west'",
"components": {
"minecraft:transformation": {
"rotation": [0, 90, 0]
}
}
},
{
"condition": "q.block_state('minecraft:cardinal_direction') == 'east'",
"components": {
"minecraft:transformation": {
"rotation": [0, 270, 0]
}
}
}
]
}
}Pillar and Log Axis Orientation
Vanilla logs and pillars don't care whether they face North or South—they only care about which axis (X, Y, or Z) their ends point along. While vanilla blocks use a `pillar_axis` state, custom blocks can achieve the exact same behavior without any scripts by using `minecraft:placement_position` with `minecraft:block_face`.
Because clicking either the North or South face of a block should result in the log pointing horizontally along the Z axis, you can combine both checks into a single permutation condition using the logical OR operator (`||`). This cuts your permutation count from six down to three.
{
"format_version": "1.20.80",
"minecraft:block": {
"description": {
"identifier": "wiki:custom_log",
"traits": {
"minecraft:placement_position": {
"enabled_states": [
"minecraft:block_face"
]
}
}
},
"components": {
"minecraft:geometry": "minecraft:geometry.full_block",
"minecraft:material_instances": {
"*": {
"texture": "custom_log_side",
"render_method": "opaque"
},
"up": {
"texture": "custom_log_top",
"render_method": "opaque"
},
"down": {
"texture": "custom_log_top",
"render_method": "opaque"
}
}
},
"permutations": [
{
"condition": "q.block_state('minecraft:block_face') == 'up' || q.block_state('minecraft:block_face') == 'down'",
"components": {
"minecraft:transformation": {
"rotation": [0, 0, 0]
}
}
},
{
"condition": "q.block_state('minecraft:block_face') == 'north' || q.block_state('minecraft:block_face') == 'south'",
"components": {
"minecraft:transformation": {
"rotation": [90, 0, 0]
}
}
},
{
"condition": "q.block_state('minecraft:block_face') == 'east' || q.block_state('minecraft:block_face') == 'west'",
"components": {
"minecraft:transformation": {
"rotation": [0, 0, 90]
}
}
}
]
}
}Which versions this applies to
Requires format_version 1.20.20 or higher. Stable in base game; no experimental toggles are required.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log Error: [Blocks] ... minecraft:cardinal_direction - State is not defined on block | You queried `q.block_state('minecraft:cardinal_direction')` inside a permutation, or tried to declare it manually inside the `states` object, without enabling the `minecraft:placement_direction` trait in the block description. | Remove any `minecraft:` states from your custom `states` object and add them to the `enabled_states` array inside `traits` instead. |
| The block places Facing away from the player instead of toward them | By default, `minecraft:placement_direction` records the direction the player's camera is looking (if you look North, the state is set to `north`), and the front of a Blockbench model faces North (`0, 0, 0`). | Add `"y_rotation_offset": 180.0` inside the `minecraft:placement_direction` trait object so the recorded cardinal direction is flipped toward the player. |
| The block model rotates sideways, or the collision box goes outside the block space | Your custom Blockbench model is not centered at `[0, 0, 0]`, or you entered a non-90-degree angle into `minecraft:transformation`. | Ensure your geometry sits within standard block bounds centered on X and Z in Blockbench, and only use multiples of 90 (`0`, `90`, `180`, `270`, `-90`) in the `rotation` array. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
