Data Types in JSON UI Bindings
Before converting data, it helps to understand how the UI engine categorizes values inside the `bindings` array. Unlike Molang, which treats almost everything as a floating-point number, JSON UI expressions recognize four primary primitives: booleans, integers, floats, and strings. Hardcoded properties retrieved from the game engine (typically prefixed with `#`) carry a strict internal type.
When you write a binding expression (by enclosing the logic in parentheses), the final output of that expression must match the exact data type required by the target property (`binding_name_override`). For example, `#visible` and `#enabled` strictly require a boolean result, `#text` requires a string, and `#clip_ratio` requires a float between 0.0 and 1.0. Feeding an integer straight into `#text` or a string of digits into `#clip_ratio` will cause the binding to silently fail or render blank.
| Type | Common Target Properties | Coercion Rule |
|---|---|---|
| String | #text, #texture | Concatenate with an empty string ('') to force conversion from numbers. |
| Integer / Float | #clip_ratio, #size_binding_x | Multiply by 1 or 1.0 to strip strings of their text type and parse digits. |
| Boolean | #visible, #enabled, #toggle_state | Use comparison operators (=, <, >) or the 'not' operator to evaluate truthiness. |
Converting Numbers and Booleans to Strings
The most common conversion needed in custom interfaces is turning a numeric binding (like `#player_level` or a slider value) into a string so a `label` control can render it. If you bind a numeric property directly to `#text` without an expression, the game will often refuse to render it because of a type mismatch.
To cast a number or boolean into a string, concatenate it with an empty string literal (`''`) using the `+` operator. In JSON UI expressions, single quotes denote a string literal, and when one side of the `+` operator is a string, the engine automatically converts the other operand into its string representation. Note that converting a float to a string this way may expose raw decimal places, so it is best used with integer bindings or pre-formatted values.
{
"namespace": "hud",
"level_readout_label": {
"type": "label",
"text": "#text",
"bindings": [
{
"binding_name": "#level_number",
"binding_name_override": "#level_raw"
},
{
"binding_type": "view",
"source_property_name": "('Level: ' + #level_raw)",
"target_property_name": "#text"
}
]
}
}Parsing Strings into Numbers
When building custom server forms, scoreboards, or reading data passed through custom title strings, your only data source is often a string such as `#title_text` or `#form_button_text`. If you want to use a numeric value hidden inside that text to drive a health bar's `#clip_ratio` or dynamically resize an element, you have to convert the string into a number.
You can force the UI engine to parse a string of digits into an integer or float by performing arithmetic on it—specifically, multiplying it by `1` (for an integer) or `1.0` (for a float), or adding `0` to it. If the string contains non-numeric characters (such as formatting codes, spaces, or letters), the conversion will fail and evaluate to zero. Because of this, you commonly pair string-to-number casting with `-` (which acts as string removal when used between two strings) to strip out unwanted prefixes before multiplying.
{
"namespace": "server_form",
"custom_mana_bar": {
"type": "image",
"texture": "textures/ui/mana_full",
"clip_direction": "left",
"bindings": [
{
"binding_name": "#form_button_text",
"binding_name_override": "#raw_text"
},
{
"binding_type": "view",
"source_property_name": "(((#raw_text - 'mana:') * 1.0) / 100.0)",
"target_property_name": "#clip_ratio"
}
]
}
}Evaluating to Booleans for Visibility
Controlling whether a UI panel is shown or hidden relies on the `#visible` property, which exclusively accepts a boolean. You cannot pass a string or an integer straight into `#visible` and expect C-style truthiness (where `1` is true and `0` is false) to work reliably across all control types.
To convert a number into a boolean, compare it against a literal number using `=`, `>`, or `<`. For strings, compare the binding against an empty string `''` or a known keyword. If you need an 'is not equal to' check, wrap an equality check in the `not` operator, such as `(not (#item_name = ''))`. You can also convert a boolean back into an integer (1 or 0) for math operations by multiplying the boolean expression by `1`, which lets you build branchless conditional math inside a single view binding.
{
"namespace": "crafting",
"conditional_warning_panel": {
"type": "panel",
"bindings": [
{
"binding_name": "#hover_text",
"binding_name_override": "#item_info"
},
{
"binding_type": "view",
"source_property_name": "(not (#item_info = ''))",
"target_property_name": "#visible"
}
]
}
}Multi-Step Conversion Chains
Complex UI modifications often require chaining multiple conversions together across separate `view` bindings on the same control. Trying to strip a string, cast it to a float, divide it, and compare it to a threshold inside one massive `source_property_name` string quickly becomes unreadable and can trip up the expression parser.
Instead, break the conversion into discrete steps using custom intermediate property names (any arbitrary name starting with `#`). First, extract and clean the string into an intermediate property. Second, cast that property into a number by multiplying by `1.0`. Finally, feed that numeric property into your target calculation. The UI engine resolves these view bindings in the order they are needed, keeping your JSON clean and easy to debug.
{
"namespace": "hud",
"parsed_data_bar": {
"type": "image",
"texture": "textures/ui/bar_fill",
"clip_direction": "left",
"bindings": [
{
"binding_name": "#title_text",
"binding_name_override": "#raw_title"
},
{
"binding_type": "view",
"source_property_name": "(#raw_title - 'HP:')",
"target_property_name": "#clean_str"
},
{
"binding_type": "view",
"source_property_name": "(#clean_str * 1.0)",
"target_property_name": "#hp_float"
},
{
"binding_type": "view",
"source_property_name": "(#hp_float / 20.0)",
"target_property_name": "#clip_ratio"
},
{
"binding_type": "view",
"source_property_name": "(#hp_float > 0.0)",
"target_property_name": "#visible"
}
]
}
}Which versions this applies to
Applies to all modern Minecraft Bedrock versions (1.16.0 and above). JSON UI is a stable resource pack system and requires no experimental toggles.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| A label control is completely blank or shows the literal binding name (like '#level_raw') on screen instead of the value. | You either forgot to wrap the `source_property_name` expression in outer parentheses `(...)`, or you passed a raw integer/float binding directly to `#text` without converting it to a string first. | Ensure the entire expression is enclosed in parentheses and concatenate an empty string in single quotes: `('' + #my_number_binding)`. |
| A custom progress bar using `#clip_ratio` is stuck at 0% (invisible) or 100% (full) even though the source value is changing. | Both numbers in your division expression are integers (or strings cast to integers via `* 1`), causing the JSON UI engine to perform integer division, which truncates `50 / 100` down to `0`. | Force floating-point division by multiplying your string or integer by `1.0` instead of `1`, and always include `.0` on your divisor (for example, `((#val * 1.0) / 100.0)`). |
| String-to-number conversion via `* 1` always evaluates to 0 when reading text from a server or scoreboard. | The source string contains hidden formatting codes (such as `§r` or `§f`) or whitespace, which causes the numeric parser to abort and return 0. | Strip out any known color codes or prefixes using the string subtraction operator (`#raw_text - '§r'`) before multiplying by `1`, or send clean, unformatted digits from your script. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
