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:
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:
The [p] prefix is checked if require-prefix.placeholders is enabled
PlaceholderAPI processes placeholders first (if enabled)
MiniPlaceholders resolvers are added (if enabled)
Both work together through tag resolvers
Enabling placeholder hooks
You must explicitly enable placeholder support in the config:
placeholder-hook :
placeholderapi : true
miniplaceholders : true
Install placeholder plugins
Download and install PlaceholderAPI and/or MiniPlaceholders on your server.
Enable hooks in config
Set the plugins you want to true in the placeholder-hook section.
Reload Runway
Run /runway reload to apply the configuration changes.
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:
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:
Basic player data
Location data
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%"
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):
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:
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:
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:
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!"
Runway processes text in this order:
Check for [mm] prefix (if required)
Remove legacy color codes (if enabled)
Remove [mm] and [p] prefixes
Process PlaceholderAPI placeholders
Process MiniPlaceholders
Deserialize MiniMessage formatting
PlaceholderAPI and MiniPlaceholders handle their own caching. Runway doesn’t cache results since player data changes frequently.
Disabling unused features
Only enable the placeholder hooks you need: placeholder-hook :
placeholderapi : true # Only if you use PAPI
miniplaceholders : false # Disable if not needed
Troubleshooting
Verify PlaceholderAPI/MiniPlaceholders is installed
Check that hooks are enabled in config.yml
Ensure the [p] prefix is present (if required)
Reload Runway with /runway reload
Test with /runway parse [mm][p]%player_name%
Placeholder shows as literal text
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
MiniMessage works but placeholders don't
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