Bedrock Wiki
  • Discord
  • Contribute
  • bedrock.dev
  • MS Learn
Beginner's Guide
  • Guide
    • 1. Introduction
      guide
    • 2. Add-Ons Explained
    • 3. Software & Preparation
    • 4. Project Setup
    • 5. Create a Custom Item
    • 6. Create a Custom Entity
    • 7. Blockbench: Modeling, Texturing & Animating
    • 8. Adding a Loot Table, Spawn Rule & Crafting Recipe
  • Extra
    • a. Understanding JSON
    • b. Download Example Packs
    • c. Troubleshooting
      help
    • d. Advanced Manifest
    • e. Format Versions
    • f. Project Setup Android
Animation Controllers
  • Intro to Animation Controllers
    guide
  • Entity Commands
  • AFK Detector
  • Death Commands
  • Molang into Scoreboard
  • Respawn Commands
Blocks
  • General
    • Intro to Blocks
      guide
    • Block Components
    • Block Tags
    • Block States
    • Block Traits
    • Block Permutations
    • Block Events
      Scripts
    • Block Event Migration
      help
    • Blocks as Items
    • Troubleshooting Blocks
      help
  • Visuals
    • Block Culling
    • Block Models
      guide
    • Block Texture Animation
    • Block Texture Variation
    • Block Tinting
  • Tutorials
    • Applying Constant Effects
      Scripts
    • Avoiding State Limit
    • Fake Blocks
      Scripts
    • Ore Loot Tables
      Scripts
    • Precise Interaction
      Scripts
    • Precise Rotation
      Scripts
    • Rotatable Blocks
  • Vanilla Re-Creations
    • Custom Crops
      Scripts
    • Custom Glass
    • Custom Glazed Terracotta
    • Custom Trapdoors
      Scripts
  • Documentation
    • Block Format History
    • Block Shapes
    • Block Sounds
    • Vanilla Block Models
Commands
  • General
    • Intro to Command Blocks
    • Functions
    • Block States
    • Coordinate System
    • NBT Commands
    • Scoreboard Operations
    • Understanding Selectors
  • Commands
    • Damage
    • Execute
    • Playanimation
    • Playsound
  • On Event Systems
    • On Player First Join
    • On Player Join
    • On Player Leave
    • On Player Death
    • On Player Respawn
    • On First World Load
  • Scoreboard Systems
    • Entity Counter
    • Scoreboard Timers
    • Comparing And Retrieving Scores
  • Techniques
    • Execute Logic Gates
    • MBE - Max's Block Entity
    • FMBE - A New Way to Create Display Entities
    • Look Detection
    • Movement Detections
    • Orbital Camera
  • Useful Creations
    • Custom Crafter
    • Multiplayer Position Rearrangement
      function
Concepts
  • contents.json
  • Emojis & Symbols
  • Molang
  • Namespaces
  • Overwriting Assets
  • Raw Text
  • Shaders
  • Sounds
  • Subpacks
  • Text and Localization
  • Texture Atlases
  • textures_list.json
Documentation
  • Shared Constructs
  • Advanced Molang
  • File Types
  • Fog IDs
  • Material Configuration Description
  • Menu Categories
  • Molang Queries
  • Pack Folder Structure
  • Sound Definitions
  • Vanilla Materials
Entities
  • General
    • Intro to Entities BP
      guide
    • Intro to Entities RP
      guide
    • Troubleshooting Entities
      help
    • Entity Events
    • Entity Properties
    • NPC Dialogues
    • Render Controllers
    • Spawn Rules
  • Tutorials
    • Convert Points Between Any Space (World, Entity, Bones)
    • Creating Boats
    • Detecting Other Entities
    • Disabling Team Damage
    • Dummy Entities
    • Entity Attacks
    • Entity Holds Item
    • Entity Movement
    • Entity Timers
    • Flying Entities
    • Introduction to AOE Clouds
    • Invulnerable Entities
    • Look at Entity
    • Sleeping Entities
    • Solid Entities
    • Spawning Tamed Entities
      Scripts
    • Village Mechanic
  • Documentation
    • Dummy Components
    • Non-Mob Runtime Identifiers
    • Projectiles
    • Runtime Identifiers
    • Vanilla Usage Components
    • Vanilla Usage Spawn Rules
Items
  • General
    • Intro to Items
      guide
    • Item Components
    • Item Tags
    • Item Events
      Scripts
    • Item Event Migration
      help
    • Troubleshooting Items
      help
  • Tutorials
    • Custom Armor
    • Custom Food
      Scripts
    • Custom Pottery Sherds
    • Custom Weapons
    • Equipment-Based Commands
    • High Resolution Items
    • Spawning Items
    • Throwable Items
  • Documentation
    • Enchantments
    • Attachables
    • Item Format History
    • Numerical Item IDs
    • Vanilla Item Identifiers
    • Vanilla Usage Components
JSON UI
  • General
    • Intro to JSON UI
      guide
    • Best Practices
      guide
  • Tutorials
    • Adding HUD Elements
    • Aseprite Animations
    • Buttons and Toggles
    • Modifying Server Forms
    • Preserve Title Texts
    • String to Number
  • Documentation
    • JSON UI Documentation
Loot, Recipes & Trading
  • General
    • Trading Behavior
  • Documentation
    • Loot Tables
    • Trade Tables
    • Recipes
    • Item Functions
  • Tutorials
    • Randomized Structure Loot
Meta
  • Add-On Performance
  • Style Guide
  • Useful Links
  • Using Schemas
  • Version Control
  • Q&A
    • Blocks and Items Q&A 2024/08/30
    • Deferred Technical Preview Q&A 2024/02/23
    • GameTest Q&A 2021/08/06
    • Scripting and Editor Q&A 2023/09/22
    • World Generation Q&A 2024/11/15
NBT
  • General
    • .mcstructure
  • Tutorials
    • Experiments in Minecraft Education
    • Extending Structure Limits
  • NBT in Depth
    • About NBT (Named Binary Tag)
    • NBT Libraries
    • Reading NBT Example
Particles
  • General
    • Intro to Particles
      guide
  • Tutorials
    • Disabling Particles
  • Documentation
    • Vanilla Particles
Scripting
  • General
    • Intro to Scripting
    • What is Script API?
    • API Modules
  • Tutorials
    • Block Placement Prevention
    • GameTests
    • Script Core Features
    • Script Forms
    • Script Requests API
    • Simple Chat Commands
  • Documentation
    • Engine Environment
    • Script Resources
    • Script Watchdog
    • Troubleshooting JavaScript
    • TypeScript
Servers
  • Software
    • Bedrock Server Software
  • Protocols
    • Bedrock Protocol
    • NetherNet Protocol
    • RakNet Protocol
Visuals
  • General
    • Introduction to Entity Visuals
      guide
    • Bedrock Modeling
    • Custom Death Animations
    • Effects in Animations
    • Material Creations
    • Materials
    • Math-Based Animations
    • Skin Packs
  • Tutorials
    • Entity Texture Animation
    • Glowing Entity Texture
    • Hurt Animations
    • Leash Position
    • Player Geometry
    • Remove Entity Shadows
    • Retexturing Spawn Eggs
  • Ideas
    • Structure Presentation
World Generation
  • General
    • Intro to World Generation
      guide
    • Biomes
      guide
    • Feature Types
    • Jigsaw Structures
  • Tutorials
    • Block Conditions for Features
    • Generating Custom Ores
    • Generating Custom Structures
    • Generating Patches
    • Heightmap Noise
  • Documentation
    • Biome Tags

Best Practices

guide
Best Practices
  • Maximizing Compatibility and Minimizing the Likelihood of the UI Breaking
    • Only Modify What is Necessary
    • Utilize the Modification Strategies
    • Avoid Modifying Controls in Nested Trees
    • Utilize a Single Entry Point
    • Avoid Working in Vanilla Namespaces
  • Maximizing Performance
    • Minimize the Number of Operators in the UI
    • Minimize the Number of Bindings in the UI
    • Avoid Adding Unneeded Controls

Info

This content of this article assumes that you are somewhat familiar with the JSON-UI system. If you are new to JSON-UI, be sure to familiarize yourself by reading the JSON-UI Intro and the JSON-UI Documentation.

Maximizing Compatibility and Minimizing the Likelihood of the UI Breaking ​

JSON-UI is different than all other add-on systems, as JSON-UI is unversioned. Any changes that you make to the UI are subject to become broken as Mojang updates and fixes the JSON-UI system. Fortunately, there are several things that you can do to prevent your UI from breaking as Mojang makes changes to the vanilla UI.

Only Modify What is Necessary ​

The most effective way to minimize the risk of your UI from breaking is to only make the changes that you are wanting to make. For example, if you only want to disable the XP bar shadow, you may think you should add this to the hud_screen.json file in your pack.

json
{
    "progress_text_label": {
        "type": "label",
        "shadow": false,
        "text": "#level_number",
        "color": "$experience_text_color",
        "anchor_from": "top_middle",
        "anchor_to": "bottom_middle",
        "bindings": [
            {
                "binding_name": "#level_number",
                "binding_type": "global"
            },
            {
                "binding_name": "#level_number_visible",
                "binding_type": "global",
                "binding_name_override": "#visible"
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

This is technically correct, but this is prone to breaking for several reasons: What if Mojang changes the name of the bindings? What if they change the anchors? What if Mojang adds an offset to the element? The changes you make are strategically merged with the vanilla UI, so including the extra details are redundant and are likely to lead to your custom UI breaking. This can simply be avoided by only including the shadow property in the element:

json
{
    "progress_text_label": {
        "shadow": false
    }
}
1
2
3
4
5

Not only is this less prone to breaking in the future, but it is also cleaner to look at and drastically reduces the file size.

By only modifying what is necessary, you are reducing the number of potential points of failure in the UI which drastically helps to prevent breaking your custom UI as the vanilla UI gets updated. As a final point, if you are including all the contents of a vanilla UI file in your pack with your changes, you are doing JSON-UI wrong.

Utilize the Modification Strategies ​

Using the modification strategies documented on the wiki are another great way to reduce the chance of breaking changes when Mojang updates the UI. For example, many add-on creators add elements to the HUD to display gameplay related information. A common tactic is to merge custom UI (custom_ui@namespace.custom_ui) into the root panel of hud_screen.json.

json
{
    "root_panel": {
        "type": "panel",
        "$xp_control_offset|default": [ 0, -13 ],
        "variables": [
            {
                "requires": "$education_edition",
                "$left_helpers": "hud.left_helpers_edu"
            },
            {
                "requires": "(not $education_edition)",
                "$left_helpers": "hud.left_helpers"
            }
        ],
        "controls": [
            {
                "custom_ui@namespace.custom_ui": {} // <--- people tend to add custom UI here!
            },
            { "left_helpers@$left_helpers": {} },
            { "right_helpers@hud.right_helpers": {} },
            { "emote_helpers@hud.emote_helpers": {} },
            { "centered_gui_elements@centered_gui_elements": {} },
            { "centered_gui_elements_at_bottom_middle@centered_gui_elements_at_bottom_middle": {} }
            ... // the rest of the controls are continued here.
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

By directly merging your custom controls with the vanilla root panel, you are drastically increasing the odds that the UI breaks in the future. For example, if Mojang changes the control names in the root panel in the future, your UI could reference UI elements that do not exist or are significantly changed, which can cause errors and/or crashing.

To avoid this, utilize the modification strategies.

json
{
    "root_panel": {
        "modifications": [
            {
                "array_name": "controls",
                "operation": "insert_front",
                "value": [
                    {
                        "custom_ui@namespace.custom_ui": {}
                    }
                ]
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Modifications using the modifications array are strategically merged with the vanilla UI, and any other packs you are using without altering the sibling controls in the root panel. This increases compatibility with other resource packs and decreases the chance of your UI breaking.

Avoid Modifying Controls in Nested Trees ​

Another common point of failure is modifying deeply nested controls. Here is an example UI element with nested controls.

json
{
    "label": {
        "type": "label",
        "text": "hello world",
        "color": [1, 1, 1]
    },

    "bg_image": {
        "type": "image",
        "texture": "textures/ui/Black",
        "alpha": 0.7
    },

    "panel_with_label_and_bg": {
        "type": "panel",
        "size": ["100%c", "100%c"],
        "controls": [
            {
                "bg_image@bg_image": {
                    "size": ["100%c + 2px", "100%c + 2px"],
                    "controls": [
                        {
                            "label@label": {
                                "layer": 5
                            }
                        }
                    ]
                }
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

To best modify this UI, you should avoid nested trees where possible. For example, if you wanted to modify the label color to be grey instead of white and the bg image to be transparent, modify the color and opacity on the element definition rather than in the tree (remember to modify only what is needed!):

json
{
    "label": {
        "color": [0.5, 0.5, 0.5]
    },

    "bg_image": {
        "alpha": 0
    }
}
1
2
3
4
5
6
7
8
9

However, sometimes it is not feasible to avoid modifying trees. In this case, you should use the following syntax to target specific controls in nested trees. For example, to modify the bg image size and label layer, use the following syntax.

json
{
    "panel_with_label_and_bg/bg_image": {
        "size": ["100%c", "100%c"]
    },

    "panel_with_label_and_bg/bg_image/label": {
        "layer": -5
    }
}
1
2
3
4
5
6
7
8
9

The / will target a child control of the specified element. Do note that if the specified target child control name does not exist, it will cause a resource pack error. Your UI will function as normal without issue, but it is preferred to avoid targeting controls in nested trees where possible due to that.

Utilize a Single Entry Point ​

In order to add custom UI to a particular screen, the UI needs to be merged with the vanilla UI at some point. This is called the entry point, and it is best practice to have your custom UI merged with the vanilla UI in a single point where possible to reduce the likelihood of the UI breaking. Here is an example that uses 2 entry points in hud_screen.json:

json
{
    "root_panel": {
        "modifications": [
            {
                "array_name": "controls",
                "operation": "insert_front",
                "value": [
                    {
                        "custom_ui_control_1@namespace_1.custom_ui_control_1": {}
                    }
                ]
            }
        ]
    },

    "hud_content": {
        "modifications": [
            {
                "array_name": "controls",
                "operation": "insert_front",
                "value": [
                    {
                        "custom_ui_control_2@namespace_2.custom_ui_control_2": {}
                    }
                ]
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

We can reduce the number of entry points to one and consolidate custom_ui_control_1 and custom_ui_control_2 into either the hud_content or root_panel control by doing the following:

json
{
    "root_panel": {
        "modifications": [
            {
                "array_name": "controls",
                "operation": "insert_front",
                "value": [
                    {
                        "custom_ui_control_1@namespace_1.custom_ui_control_1": {}
                    },
                    {
                        "custom_ui_control_2@namespace_1.custom_ui_control_2": {}
                    }
                ]
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Using a single entry point reduces the likelihood of UI breaking because if Mojang updated the hud_content control name, some custom UI may have broken. Using a single entry point also makes your UI easier to debug since you only need to account for one entry point.

Avoid Working in Vanilla Namespaces ​

If you are modifying large parts of the UI or are adding a large amount of custom UI, you should avoid working in the vanilla namespaced files where possible. You can do this by adding your custom UI file with a unique namespace to the UI Definition File. Remember, you can reference other elements in other namespaces by using the element@namespace.element syntax in the control definition when you need to merge your UI into an entry point. By adding your custom UI in a custom namespace, you are reducing the likelihood of a vanilla control name collision, which can cause issues. Additionally, like most other add-on systems, a namespace can support a prefix, such as wiki:namespace that can be referenced as element@wiki:namespace.element. A prefix can also help to avoid collisions with vanilla namespaces.

Maximizing Performance ​

In terms of FPS, JSON-UI is the second most costly add-on subsystem behind entities. Have you ever wondered why the opening the inventory can cut your FPS in half? The short answer is that JSON-UI is incredibly unoptimized, leading to lower FPS. Adding a lot custom UI can have the potential to add significant overhead, leading to persistent reduced in-game frame rates, long screen loading times, and general poor user experience.

Minimize the Number of Operators in the UI ​

Operators are used in the UI to evaluate conditions, perform math, and modify strings. These operators are useful for techniques such as conditional rendering, but using these add a lot of overhead. For example, if you have a variable "$var": "(2 * (-1 * $number))", it is significantly more performant to simplify to "$var": "(-2 * $number)". It is best to simplify expressions as much as possible, and remove unneeded expressions in order to speed up evaluation times as much as possible.

Minimize the Number of Bindings in the UI ​

Similar to operators, using many bindings can also add significant overhead. Part of the reason why the settings screen takes so long to open is because all of the toggles and options are linked to specific bindings, in which there are many. Removing bindings that do not contribute to function, or can be simplified are another fantastic way to increase performance.

Avoid Adding Unneeded Controls ​

Perhaps the best way to increase performance in JSON-UI is to delete unused or unnecessary controls. In this example, the child control panel isn't needed because it is an empty panel.

json
{
    "element": {
        "type": "image",
        "texture": "textures/ui/Black",
        "controls": [
            {
                "panel": {
                    "type": "panel"
                }
            },
            {
                "label": {
                    "type": "label",
                    "text": "hello world"
                }
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

To fix this, you can either delete it from the UI tree or add "ignored": true to the panel control. Using "ignored": true" is the same as deleting it. The control and all child controls will not be evaluated in UI, leading to the same performance as if it was not there at all. Using "visible": false does not have the same effect and the controls will still be evaluated.

json
{
    "element": {
        "type": "image",
        "texture": "textures/ui/Black",
        "controls": [
            {
                "label": {
                    "type": "label",
                    "text": "hello world"
                }
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
json
{
    "element": {
        "type": "image",
        "texture": "textures/ui/Black",
        "controls": [
            {
                "panel": {
                    "type": "panel",
                    "ignored": true
                }
            },
            {
                "label": {
                    "type": "label",
                    "text": "hello world"
                }
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Sometimes, you can simplify and consolidate multiple controls into a single element. For example, if you wanted to render a specific image based on the #hud_title_text_string being a specific number 1-5, you may think you should add 5 separate controls to be evaluated separately:

json
{
    "image_template": {
        "type": "image",
        "texture": "$texture",
        "bindings": [
            {
                "binding_name": "#hud_title_text_string"
            },
            {
                "binding_type": "view",
                "source_property_name": "(#hud_title_text_string = $binding_text)",
                "target_property_name": "#visible"
            }
        ]
    },

    "image_1@image_template": {
        "$texture": "textures/ui/example_1",
        "$binding_text": "1"
    },

    "image_2@image_template": {
        "$texture": "textures/ui/example_2",
        "$binding_text": "2"
    },

    "image_3@image_template": {
        "$texture": "textures/ui/example_3",
        "$binding_text": "3"
    },

    "image_4@image_template": {
        "$texture": "textures/ui/example_4",
        "$binding_text": "4"
    },

    "image_5@image_template": {
        "$texture": "textures/ui/example_5",
        "$binding_text": "5"
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

Taking a more thoughtful approach, this can be significantly simplified into a single control with fewer operators, bindings, and controls overall.

json
{
    "image": {
        "type": "image",
        "texture": "#texture",
        "bindings": [
            {
                "binding_name": "#hud_title_text_string"
            },
            {
                "binding_type": "view",
                "source_property_name": "(((#hud_title_text_string * 1) > 0) and ((#hud_title_text_string * 1) < 6))",
                "target_property_name": "#visible"
            },
            {
                "binding_type": "view",
                "source_property_name": "('textures/ui/example_' + #hud_title_text_string)",
                "target_property_name": "#texture"
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Overall, in order to improve performance of the UI, it is important to understand what exactly you are trying to do and make adjustments that fall in line with these general guidelines as much as possible. Leaving a single control unoptimized likely won't make much of a noticeable difference. However when you start leaving many controls unoptimized, performance may start to suffer on top of an already unoptimized UI system.

Contributors

Edit Best Practices on GitHub

Bedrock Wiki by Bedrock OSS

"Minecraft" is a trademark of Mojang AB.

Bedrock OSS, Bedrock Wiki and bedrock.dev are not affiliated in any way with Microsoft or Mojang AB.

  • Privacy Policy
  • Join our Discord
  • Learn how to Contribute
  • Visit our Repository