Skip to main content

Overview

Runway supports both PlaceholderAPI and MiniPlaceholders, allowing you to create dynamic text that combines:
  • MiniMessage formatting (colors, gradients, hover, click events)
  • Dynamic placeholders (player stats, server info, custom data)
  • Static custom placeholders from config
PlaceholderAPI and MiniPlaceholders are optional dependencies. Install them only if you need placeholder support.

Supported placeholder systems

PlaceholderAPI

The most popular placeholder plugin with 1000+ expansions

MiniPlaceholders

Modern placeholder system with MiniMessage-native support
Both can be used simultaneously, giving you access to all available placeholders.

The [p] prefix

Similar to the [mm] prefix, Runway uses [p] to identify text that should have placeholders parsed. This prefix can appear anywhere in the text and is removed during processing.
message: "[mm][p]<gradient:gold:yellow>Balance:</gradient> <white>%vault_eco_balance%"
You can use [mm] and [p] together in the same message. The order doesn’t matter.

How the prefix works

From ProcessHandler.java:86-98:
ProcessHandler.java
boolean miniPlaceholders = config.getOrDefault("placeholder-hook.miniplaceholders", false);
boolean placeholderapi = config.getOrDefault("placeholder-hook.placeholderapi", false);
boolean requirePrefixP = config.getOrDefault("require-prefix.placeholders", true);

if (player != null && (miniPlaceholders || placeholderapi)) {
    if (requirePrefixP && !s.contains("[p]")) {
        return miniMessage.deserialize(s, placeholdersResolver);
    }

    s = s.replace("[p]", "");
    if (placeholderapi) s = PlaceholderAPI.setPlaceholders(player, s);
    TagResolver resolver = placeholdersResolver;
    if (miniPlaceholders) {
        TagResolver playerResolver = MiniPlaceholders.getAudienceGlobalPlaceholders(player);
        resolver = TagResolver.builder().resolvers(playerResolver, placeholdersResolver).build();
    }
    return miniMessage.deserialize(s, resolver);
}
The code shows that:
  1. The [p] prefix is checked if require-prefix.placeholders is enabled
  2. PlaceholderAPI processes placeholders first (if enabled)
  3. MiniPlaceholders resolvers are added (if enabled)
  4. Both work together through tag resolvers

Enabling placeholder hooks

You must explicitly enable placeholder support in the config:
config.yml
placeholder-hook:
  placeholderapi: true
  miniplaceholders: true
1

Install placeholder plugins

Download and install PlaceholderAPI and/or MiniPlaceholders on your server.
2

Enable hooks in config

Set the plugins you want to true in the placeholder-hook section.
3

Reload Runway

Run /runway reload to apply the configuration changes.
4

Test placeholders

Use the parse command to test: /runway parse [mm][p]<player> displays player name.

Disabling the prefix requirement

If you want placeholders to be processed in all messages:
config.yml
require-prefix:
  placeholders: false  # Parse placeholders in all text
Disabling the prefix may impact performance, as every message will be checked for placeholders even if none are present.

PlaceholderAPI examples

PlaceholderAPI uses the %placeholder% syntax:

Player information

welcome: "[mm][p]<gradient:green:blue>Welcome, %player_name%!</gradient>"
health: "[mm][p]<red>❤</red> <white>%player_health%/%player_max_health%"
ping: "[mm][p]<gray>Ping: <green>%player_ping%ms"

Economy integration

balance: "[mm][p]<gradient:gold:yellow>Balance:</gradient> <white>$%vault_eco_balance%"
top-balance: "[mm][p]<gold>#1 Richest:</gold> <white>%vault_top_balance_1%"
group: "[mm][p]<gray>Rank: <gradient:red:yellow>%vault_group%</gradient>"

Statistics and gameplay

kills: "[mm][p]<red>⚔</red> <white>%statistic_player_kills% kills"
deaths: "[mm][p]<dark_gray>☠</dark_gray> <white>%statistic_deaths% deaths"
playtime: "[mm][p]<green>Playtime: %statistic_time_played%"

Third-party plugin placeholders

faction: "[mm][p]<gold>Faction:</gold> <white>%factions_faction%"
power: "[mm][p]<red>Power:</red> <white>%factions_power%/%factions_power_max%"
Visit the PlaceholderAPI wiki for a complete list of available placeholders.

MiniPlaceholders examples

MiniPlaceholders uses the <placeholder> tag syntax:

Player data

welcome: "[mm][p]<gradient:blue:cyan>Hello, <player>!</gradient>"
ping: "[mm][p]<gray>Your ping: <green><player:ping>ms</green>"
health: "[mm][p]<red>❤ <player:health>/<player:max_health>"

Server data

players: "[mm][p]<gray>Players: <green><server:online>/<server:max></green>"
motd: "[mm][p]<gradient:gold:yellow><server:motd></gradient>"
version: "[mm][p]<gray>Running <white><server:version>"

Relational placeholders

MiniPlaceholders excels at relational placeholders (data between two players):
targeted: "[mm][p]<gray>You are looking at <player:targeted>"
distance: "[mm][p]<gray>Distance: <rel:distance:targeted>m"
Relational placeholders require context about multiple players. They work best in features like tab lists or targeted messages.

Combining MiniMessage with placeholders

The real power comes from mixing formatting with dynamic data:

Gradients with placeholders

level-up: "[mm][p]<gradient:gold:yellow><bold>LEVEL UP!</bold></gradient><newline><gray>You are now level <green>%player_level%"

Hover text with placeholders

player-info: "[mm][p]<hover:show_text:'<gold>Level:</gold> <white>%player_level%<newline><gold>Health:</gold> <red>%player_health%</red>'>%player_name%</hover>"

Click events with placeholders

teleport: "[mm][p]<click:run_command:/tp %player_name%><hover:show_text:'<green>Click to teleport!'><green>Teleport to %player_name%</hover></click>"

Complex scoreboard

sidebar:
  title: "[mm]<gradient:red:yellow><bold>SERVER</bold></gradient>"
  lines:
    - "[mm][p]<gray>━━━━━━━━━━━━━━"
    - "[mm][p]<gold>Rank:</gold> <gradient:red:yellow>%vault_group%</gradient>"
    - "[mm][p]<gold>Balance:</gold> <green>$%vault_eco_balance%"
    - "[mm][p]<gold>Playtime:</gold> <white>%statistic_time_played%"
    - "[mm][p]<gray>━━━━━━━━━━━━━━"
    - "[mm][p]<gray>Players: <green>%server_online%</green>/<green>%server_max_players%</green>"

Advanced tab list

header: "[mm][p]<gradient:gold:yellow><bold>EXAMPLE SERVER</bold></gradient><newline><gray>Players: <green>%server_online%/%server_max_players%</green>"

Custom static placeholders

Runway allows you to define custom placeholders in a separate config file. These are processed through the placeholdersResolver (ProcessHandler.java:37-50):
ProcessHandler.java
public void reloadPlaceholders() {
    TagResolver.Builder builder = TagResolver.builder();

    for (String key : placeholders.singleLayerKeySet("custom-placeholders")) {
        @Subst("") String placeholder = key.toLowerCase()
                                            .replace(" ", "_")
                                            .replace("-", "_");

        String value = placeholders.getString("custom-placeholders." + key);

        builder.resolver(Placeholder.parsed(placeholder, value));
    }
    placeholdersResolver = builder.build();
}
Create a placeholders.yml file:
placeholders.yml
custom-placeholders:
  server-name: "<gradient:red:yellow>Epic Server</gradient>"
  discord-link: "<click:open_url:'https://discord.gg/example'><blue>discord.gg/example</blue></click>"
  website: "<click:open_url:'https://example.com'><green>example.com</green></click>"
  rule-1: "<gray>1. Be respectful to all players"
  rule-2: "<gray>2. No griefing or stealing"
Use them with MiniMessage tag syntax:
welcome: "[mm]Welcome to <server_name>!"
social: "[mm]Join us: <discord_link> • <website>"
rules: "[mm]<rule_1><newline><rule_2>"
Custom placeholders are perfect for frequently used text snippets like server names, links, or standard messages.

Tab list implementation

The TablistListener (TablistListener.java:15-24) processes both header and footer:
TablistListener.java
public void onPacketPlaySend(PacketPlaySendEvent e) {
    if (!config.getOrDefault("listeners.tablist", true) ||
        e.getPacketType() != PacketType.Play.Server.PLAYER_LIST_HEADER_AND_FOOTER) return;

    Player player = e.getPlayer();
    WrapperPlayServerPlayerListHeaderAndFooter packet = new WrapperPlayServerPlayerListHeaderAndFooter(e);
    packet.setFooter(handler.processComponent(packet.getFooter(), player));
    packet.setHeader(handler.processComponent(packet.getHeader(), player));
}
This means any tab list plugin that sets headers/footers will automatically support MiniMessage and placeholders.

Item lore with placeholders

The ItemListener (ItemListener.java:16-24) processes item display names and lore:
ItemListener.java
public void onPacketPlaySend(PacketPlaySendEvent e) {
    if (!config.getOrDefault("listeners.items", true) ||
        e.getPacketType() != PacketType.Play.Server.SET_SLOT) return;

    Player player = e.getPlayer();
    WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(e);
    packet.setItem(handler.processItem(packet.getItem(), player));
}
Example item with dynamic lore:
name: "[mm]<gradient:gold:yellow><bold>Player Stats</bold></gradient>"
lore:
  - "[mm][p]<gray>Name: <white>%player_name%"
  - "[mm][p]<gray>Level: <green>%player_level%"
  - "[mm][p]<gray>Balance: <gold>$%vault_eco_balance%"
  - "[mm][p]<gray>Kills: <red>%statistic_player_kills%"
  - ""
  - "[mm]<dark_gray><italic>Updates in real-time!"

Performance considerations

Runway processes text in this order:
  1. Check for [mm] prefix (if required)
  2. Remove legacy color codes (if enabled)
  3. Remove [mm] and [p] prefixes
  4. Process PlaceholderAPI placeholders
  5. Process MiniPlaceholders
  6. Deserialize MiniMessage formatting
PlaceholderAPI and MiniPlaceholders handle their own caching. Runway doesn’t cache results since player data changes frequently.
Only enable the placeholder hooks you need:
placeholder-hook:
  placeholderapi: true   # Only if you use PAPI
  miniplaceholders: false  # Disable if not needed

Troubleshooting

  1. Verify PlaceholderAPI/MiniPlaceholders is installed
  2. Check that hooks are enabled in config.yml
  3. Ensure the [p] prefix is present (if required)
  4. Reload Runway with /runway reload
  5. Test with /runway parse [mm][p]%player_name%
If you see %player_name% literally:
  • The placeholder expansion isn’t installed (use /papi ecloud download <expansion>)
  • The placeholder syntax is incorrect
  • PlaceholderAPI hook is disabled in config
You need both prefixes:
message: "[mm][p]<green>Hello %player_name%!"
Not just:
message: "[mm]<green>Hello %player_name%!"  # Missing [p]

Next steps

MiniMessage formatting

Learn about MiniMessage syntax and formatting options

Action bar messages

Send formatted messages to the action bar

Build docs developers (and LLMs) love