Leveraging MediaWiki's Parser Functions for Advanced Templating
Why parser functions matter in modern MediaWiki templating
When you first encounter MediaWiki’s templating it feels a bit like opening a toolbox where most of the tools are hidden behind curly braces. You can get a simple infobox up and running with a handful of parameters, but as soon as you want the template to react to its inputs you’ll start hunting for something more flexible. That “something” is the suite of parser functions shipped with the ParserFunctions extension.
In a nutshell, parser functions let you embed if‑else logic, perform string tricks, and even decide which template to call – all without touching PHP or Lua. The result? templates that feel more like tiny programs than static snippets.
Quick recap: the core functions
- #if – basic true/false test.
- #ifeq / #ifneq – equality/inequality comparison.
- #ifexist – checks whether a page or template exists.
- #switch – multi‑branch selector, handy for enum‑style values.
- #expr – arithmetic and logical expressions (since 1.35).
Each of these follows the same syntax: {{#if: condition | value‑if‑true | value‑if‑false }}. The pipes separate the arguments; any argument may itself be another parser function, which opens the door to deep nesting.
Variable template names – “template‑as‑data”
One of the quirkiest tricks you can pull off is to let a parameter dictate which template gets invoked. The idea is simple: wrap a variable in double curly braces a second time.
{{{{{template_name}}}}}
Suppose you have three tiny templates: AlertSuccess, AlertWarning and AlertError. A master wrapper could look like this:
{{{!}}}
{{#if:{{{type|}}}
|{{{{{type}}}}}
|{{AlertInfo}}
}}
{{{!}}}
If the caller supplies type=AlertWarning, the outer braces evaluate AlertWarning as a template name, rendering its content. If type is missing, we fall back to a neutral AlertInfo. The pattern may look like a mind‑bender, but it’s just a handful of characters and a lot of flexibility.
When to (not) use it
Variable template names are a neat hack for quick branching, but they have limits:
- They can’t easily handle complex conditions. For that you’re better off with
#switchor a Lua module. - Debugging is a bit of a pain because the inner template name is only resolved at render time.
- Some wikis disable recursive template calls for performance reasons.
In practice, I keep this technique for small, well‑controlled sets of alternatives – like colour‑coded alerts, language‑specific, or version‑specific snippets.
Dynamic parameter names – “parameter‑as‑key”
Just as you can make the name dynamic, you can also make the parameter name dynamic. The syntax uses extra braces to treat a parameter’s value as a new placeholder:
{{{{{param_key}}}}}
Imagine a template that receives a list of field/value pairs, but the field identifier isn’t known ahead of time. Here’s a miniature example that builds a small definition list:
{{#if:{{{key1|}}}
|'''{{{key1}}}:''' {{{value1}}}
}}
{{#if:{{{key2|}}}
|'''{{{key2}}}:''' {{{value2}}}
}}
Now, by passing key1=Author and value1=Jane Doe the output becomes “Author: Jane Doe”. The trick shines when paired with #foreach-style loops from the StringFunctions extension – you can iterate over a set of name/value pairs and echo them without hard‑coding each one.
Parser functions as defaults – graceful degradation
One of the most common patterns is to provide a fallback value when a parameter isn’t supplied. You might be tempted to write a giant #if ladder, but the pipe‑syntax for default values is a lot cleaner:
{{{title|Untitled}}}
Here, if title is missing, “Untitled” appears. You can combine this with #ifexist to check whether a linked page actually exists before rendering a link:
{{#ifexist:{{{link|}}}
|[[{{{link}}}|{{{link}}}]]
|{{{link|(missing)}}}
}}
The result is a clean inline link when the target exists, and a gentle placeholder otherwise – no broken red links in the final article.
Branching without parser functions – the “manual recursion” trick
Before the ParserFunctions extension became ubiquitous, power users relied on clever template recursion. The idea is to let a template call itself with a modified parameter until a condition is met. While this approach is slower and a bit obtuse, it’s still useful when you want a pure‑wikitext solution on a wiki that can’t install extensions.
Take a simple “repeat N times” example:
{{#if:{{{count|0}}}
|{{{text}}}{{{text}}}
|{{#if:{{{count|0}}}|{{{template|Repeat}}}|}}
}}In reality the code would be longer, but the principle is: decrement count on each call and concatenate the result. If you ever need to support a restricted environment, keep this pattern in your back pocket.
String manipulation – turning text into data
Parser functions can act like a tiny scripting language for strings. The #replace function (from the StringFunctions extension) lets you swap substrings, while #explode can split a CSV‑style list into an array that you then loop over with #foreach. Here’s a compact way to turn “A,B,C” into a vertical list:
{{#foreach:{{#explode:{{{items}}}|,}}|
* {{{.}}}
}}
Notice the single dot ({{{.}}}) – it stands for the current item in the loop. This short snippet is the backbone of many “dynamic infobox” solutions where the set of rows isn’t known until the page is saved.
Combining #expr for arithmetic
If you need to calculate totals, percentages or simple conditional math, #expr is a lifesaver. It evaluates a fairly standard expression language:
{{#expr: ({{{wins}}} + {{{losses}}}) / 2 }}
Because #expr returns a plain number, you can feed it straight into other functions, e.g. to decide a colour class:
{{#ifexpr: {{#expr: {{{score}}} >= 75 }} | high | low }}
Notice the subtle difference between #ifexpr (which expects a true/false expression) and #if (which parses a plain string). Mixing them correctly avoids surprising false negatives.
Practical example: a reusable “status badge”
Below is a self‑contained template that demonstrates many of the concepts discussed:
{{{!}}}
{{#if:{{{type|}}}
|{{{{{type}}}}}
|{{BadgeInfo}}
}
{{!}}
And the supporting badge templates (BadgeInfo, BadgeSuccess, BadgeWarning, BadgeError) each look like this:
{{{!}}}
{{{label|Status unknown}}}
{{!}}
When you include the wrapper like {{BadgeWrapper|type=BadgeSuccess|class=good|label=All clear}}, the outer template decides which inner badge to load, while the inner badge decides its CSS class (falling back to “default” if the class page is missing). The whole thing stays within pure wikitext. No PHP, no Lua.
Tips for keeping your advanced templates maintainable
- Comment heavily. Use to explain why a particular
#ifbranch exists. - Prefer named parameters. They’re easier to read than positional ones, especially when you start nesting functions.
- Test with
subst:. Substituting a template into a sandbox shows the intermediate expansion steps – priceless for debugging recursion. - Avoid deep nesting. More than three layers of parser functions quickly becomes a readability nightmare.
- Document defaults. If you rely on fallback values, list them in a table at the top of the template.
Wrapping up
Parser functions let you stretch MediaWiki’s templating from simple placeholders into a lightweight logic engine. By mastering variable template names, dynamic parameter keys, default handling, and string tricks, you can build that adapt on the fly, reduce duplication, and keep the markup tidy.
Sure, there’s a learning curve – the syntax can look like a crossword puzzle at first – but once you get the hang of it, you’ll find yourself reaching for a parser function before you consider a full‑blown Lua module. And that, in my humble opinion, is the sweet spot where power meets accessibility.