Wiki 9Minecraft Wiki 9Minecraft

Dynamic Displays with Rawtext and Commands

Build live-updating action bars, titles, and floating text using tellraw, titleraw, and scoreboards.

6Sections
6Code examples
4Known errors

Dynamic displays in Minecraft Bedrock allow add-on creators to show live game data—such as timers, currency, health, and active player names—directly on the screen or in the world. By combining the rawtext JSON format with commands like titleraw, tellraw, and scoreboard, you can build custom user interfaces and floating holograms without relying on complex UI modifications or external script libraries. You reach for these recipes whenever a map or add-on needs to communicate changing state to players in real time.

The Rawtext JSON Structure

At the heart of every command-driven display in Bedrock is the rawtext JSON object. Unlike plain text commands such as say or title, rawtext commands (tellraw and titleraw) parse a JSON payload before rendering it to the screen. This payload accepts an array of component objects that the game evaluates at the exact tick the command runs, stitching the results together into a single continuous string.

Each object inside the rawtext array performs one specific job: rendering a literal string, looking up a translation key from a language file, resolving the names of entities matching a target selector, or pulling an integer value from a scoreboard objective. Because the array is evaluated sequentially, you can mix static labels, formatting codes, and live variables in a single command call.

When writing rawtext inside a behaviour pack function file (.mcfunction), the entire JSON structure must be collapsed onto a single line following the command arguments. Below is the expanded, formatted structure of a valid rawtext payload to show how the different object types sit side by side inside the main array.

Component KeyWhat it doesNotes
textRenders a literal string of characters.Supports section-sign (§) formatting codes and the \n newline escape sequence.
scoreDisplays the integer value of a tracked scoreboard objective.Requires a nested object containing both 'name' (selector or fake player) and 'objective'.
selectorPrints the display name of all entities matching a target selector.If multiple entities match (e.g., @a), the game automatically joins their names with commas.
translateLooks up a localization key in the active resource pack's .lang file.Falls back to printing the raw key string if the translation is missing from the pack.
withSupplies substitution arguments (%s or %1$s) to a companion translate key.Accepts either an array of plain strings or another nested rawtext object.
BP/functions/examples/rawtext_payload_reference.json
{
  "rawtext": [
    {
      "text": "§6Player: §f"
    },
    {
      "selector": "@p"
    },
    {
      "text": " §7| §aCoins: §f"
    },
    {
      "score": {
        "name": "@p",
        "objective": "coins"
      }
    },
    {
      "text": "\n§eStatus: "
    },
    {
      "translate": "hud.status.ready",
      "with": [
        "5"
      ]
    }
  ]
}

Live HUDs with Titleraw Actionbars

The most common place for a dynamic display is the action bar—the small region directly above the player's hotbar. Unlike the main center-screen title and subtitle slots, the action bar does not obstruct the player's crosshair and supports multiline text using the \n escape sequence. By running a titleraw command targeting the actionbar slot every tick (or every few ticks), you create a persistent heads-up display that updates immediately whenever a player's score changes.

A critical quirk arises when you try to display each player's own score to them in multiplayer. If you run a single titleraw @a actionbar command and put @s or @p inside the score component's name field, every player on the server will see the exact same score—specifically, the score of whoever executed the command or whoever was closest to the command's execution origin. This happens because the command resolves the rawtext payload once from the executor's context before broadcasting the resulting text to the target audience.

To show individualized scores, you must shift the execution context to each player individually before calling titleraw. Wrapping the command in execute as @a run titleraw @s actionbar forces the game to evaluate the rawtext payload separately for every online player. In that context, @s inside the target argument and @s (or *) inside the score component both refer to the specific player currently being processed.

BP/functions/hud/update_actionbar.mcfunction
# Ensure objectives exist before attempting to display them
scoreboard objectives add coins dummy "Coins"
scoreboard objectives add mana dummy "Mana"
scoreboard objectives add timer_min dummy "Minutes"
scoreboard objectives add timer_sec dummy "Seconds"

# Execute as each player so @s resolves to the individual viewer
execute as @a run titleraw @s actionbar {"rawtext":[{"text":"§6Coins: §e"},{"score":{"name":"@s","objective":"coins"}},{"text":"   §bMana: §f"},{"score":{"name":"@s","objective":"mana"}},{"text":"/100\n§7Time Remaining: §f"},{"score":{"name":".global","objective":"timer_min"}},{"text":":"},{"score":{"name":".global","objective":"timer_sec"}}]}

World-Space Floating Text Entities

When you want a display to exist at a physical coordinate in the world—such as a leaderboard in a lobby, a price tag above a shop chest, or a generator countdown—you use a dummy entity's custom name tag. In Bedrock Edition, any entity with an active minecraft:nameable component can display a floating string above its bounding box. By making the entity invisible and non-interactive, only the floating text plate remains visible to players.

While vanilla commands cannot directly inject a rawtext JSON object into an entity's CustomName field, you can build a custom dummy entity in your behaviour pack for the sole purpose of holding text, or use the Script API to bind rawtext strings to its nameTag property. Even without scripts, you can create state-based dynamic displays using entity events: define component groups in the entity JSON with different preset name tags, or toggle the minecraft:is_baby, minecraft:variant, or minecraft:skin_id components if your resource pack uses them to drive custom geometry or UI.

The entity definition below establishes a stable, lag-free floating text anchor. It disables gravity and collision so the text never falls or pushes players, applies the minecraft:damage_sensor component to make it completely immune to all damage sources (including /kill if you choose, though keeping it vulnerable to void/command damage is safer for cleanup), and sets always_show to true on the nameable component so players do not have to aim their crosshair directly at the invisible hitbox to read the display.

BP/entities/floating_display.json
{
  "format_version": "1.19.50",
  "minecraft:entity": {
    "description": {
      "identifier": "wiki:floating_display",
      "is_spawnable": false,
      "is_summonable": true,
      "is_experimental": false
    },
    "components": {
      "minecraft:type_family": {
        "family": [
          "display",
          "inanimate"
        ]
      },
      "minecraft:collision_box": {
        "width": 0.0,
        "height": 0.0
      },
      "minecraft:physics": {
        "has_gravity": false,
        "has_collision": false
      },
      "minecraft:pushable": {
        "is_pushable": false,
        "is_pushable_by_piston": false
      },
      "minecraft:nameable": {
        "always_show": true,
        "allow_name_tag_renaming": false
      },
      "minecraft:damage_sensor": {
        "triggers": [
          {
            "cause": "all",
            "deals_damage": false
          }
        ]
      },
      "minecraft:knockback_resistance": {
        "value": 1.0
      }
    }
  }
}

Conditional Formatting and Progress Bars

Rawtext score components only output raw integers; they cannot natively change colour when a number drops low, nor can they draw graphical progress bars on their own. To build visual bars (like [████░░░░░░]) or colour-shifting warnings in pure commands, you combine score range selectors with multiple titleraw lines.

The technique relies on the scores={} target selector argument. Instead of running one unconditional titleraw command for everyone, you write a sequence of execute commands that filter players by their exact score bracket. Each bracket runs its own tailored titleraw payload with hard-coded section-sign colour codes and block characters matching that specific percentage range.

This pattern is ideal for capture-point progress, boss health bars on the action bar, or overheating gauges. Because only one score range matches a player on any given tick, only one titleraw command fires for that player, preventing flickering. You can still embed live score components inside each bracketed command so the exact numerical readout updates smoothly alongside the stepped visual bar.

BP/functions/hud/capture_bar.mcfunction
# Display a stepped progress bar and colour-coded percentage based on 'capture' score (0 to 100)

execute as @a[scores={capture=0..24}] run titleraw @s actionbar {"rawtext":[{"text":"§cCapture Progress: [■■□□□□□□] "},{"score":{"name":"@s","objective":"capture"}},{"text":"%"}]}

execute as @a[scores={capture=25..49}] run titleraw @s actionbar {"rawtext":[{"text":"§6Capture Progress: [■■■■□□□□] "},{"score":{"name":"@s","objective":"capture"}},{"text":"%"}]}

execute as @a[scores={capture=50..74}] run titleraw @s actionbar {"rawtext":[{"text":"§eCapture Progress: [■■■■■■□□] "},{"score":{"name":"@s","objective":"capture"}},{"text":"%"}]}

execute as @a[scores={capture=75..99}] run titleraw @s actionbar {"rawtext":[{"text":"§aCapture Progress: [■■■■■■■□] "},{"score":{"name":"@s","objective":"capture"}},{"text":"%"}]}

execute as @a[scores={capture=100..}] run titleraw @s actionbar {"rawtext":[{"text":"§b§lCapture Complete: [■■■■■■■■] 100%"}]}

Localizing Dynamic Displays with Translate

Hard-coding English strings inside your .mcfunction files makes it difficult to translate your add-on later and clutters your command logic. Using the translate and with keys inside rawtext lets you move all display text into your resource pack's texts/en_US.lang file while still injecting live scoreboard numbers and player names at runtime.

In the .lang file, you define placeholders using standard positional tokens: %s for unnumbered sequential replacements, or %1$s, %2$s, and so on when you need to control the exact order in which variables appear in different languages. When the game processes a translate component in rawtext, it takes the companion with key, evaluates every element inside it, and slots the results into those %1$s tokens.

Crucially, if you want to pass dynamic data like a score or a target selector into a translation placeholder, the value of with must be a nested {"rawtext": [...]} object rather than a simple string array. Each top-level item inside that inner rawtext array becomes one positional argument (%1$s, %2$s, etc.) for the language string.

Token in .langPosition in 'with' rawtext arrayExample Rawtext Element
%1$sFirst object (Index 0){"selector": "@s"}
%2$sSecond object (Index 1){"selector": "@p[tag=bounty_target]"}
%3$sThird object (Index 2){"score": {"name": "@s", "objective": "bounty_value"}}
RP/texts/en_US.lang
display.bounty.claimed=§e%1$s §7claimed the bounty on §c%2$s §7for §6%3$s Coins§7!
display.hud.stats=§aLevel: §f%1$s §8| §dXP: §f%2$s/%3$s

Automating Display Refreshes with tick.json

A dynamic display is only as responsive as the loop that triggers it. While repeating command blocks work for single-world maps, a portable behaviour pack should drive its display functions through BP/functions/tick.json. Any function path listed in this file executes automatically once per game tick (20 times per second) from the server context.

Running heavy selector queries and titleraw commands 20 times a second for every player is generally fine for small groups, but on larger realms or servers it is cleaner to throttle your display updates. Humans rarely notice if a coin counter or timer updates every 4 or 5 ticks instead of every single tick, and throttling keeps the action bar's native fade-out timer refreshed without saturating the command execution queue.

You can build a lightweight throttle inside your main tick function by incrementing a fake player score (such as .tick_counter) by 1 every tick, running your HUD update function only when that score matches a target number, and immediately resetting the counter back to 0.

BP/functions/tick.json
{
  "values": [
    "hud/update_actionbar"
  ]
}

Which versions this applies to

Applies to Minecraft Bedrock Edition 1.19.50 and newer (using the updated execute command syntax). Rawtext commands (tellraw, titleraw) and tick.json are fully stable features and do not require any experimental toggles.

When it does not work

What you seeWhyThe fix
Score component renders as blank space in the message instead of printing '0'.In Bedrock Edition, newly joined players or freshly created objectives do not automatically assign a score of 0 to entities; the entity has 'no score' (null), which rawtext renders as an empty string.Run 'scoreboard players add @a <objective> 0' in your tick function or setup function. Adding 0 forces the game to initialize the player's score to 0 without altering existing non-zero scores.
Command outputs: 'Syntax error: Unexpected "{" at ...' or function fails to load on world start.The rawtext JSON payload contains unescaped double quotes inside a text string, is missing the outer {"rawtext":[...]} wrapper, or spans across multiple lines inside an .mcfunction file.Collapse the entire JSON object onto a single line in your .mcfunction file, verify that the root key is exactly "rawtext", and escape any internal quotation marks with a backslash (\").
Every player on the server sees the exact same score on their HUD instead of their own personal score.You executed 'titleraw @a actionbar' with '"name": "@p"' or '"name": "@s"' directly from the server or a command block, causing the score to resolve once before being broadcast to everyone.Prefix the command with 'execute as @a run titleraw @s actionbar' and use '"name": "@s"' (or '"name": "*"') inside the score object so each player evaluates the command from their own context.
Translated dynamic text displays literal '%1$s' instead of the player's score or name.You passed a standard JSON array to the 'with' field (e.g., "with": [{"score": ...}]) instead of wrapping the array inside a nested rawtext object.When passing selectors or scores into 'with', structure it as a nested rawtext payload: '"with": {"rawtext": [{"score": {"name": "@s", "objective": "coins"}}]}'.

Reference, straight from the game files

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