Mastering MediaWiki's Parser Functions: Advanced Techniques for Dynamic Content
Why Parser Functions Matter
When you first open a MediaWiki page you probably see static text, tables, and maybe a handful of links. Yet behind those simple symbols lies a tiny programming language that lets the wiki behave like a lightweight web‑app. Those are the parser functions. They aren’t just neat tricks; they’re the backbone of dynamic content, conditional formatting, and reusable logic. Mastering them feels a bit like learning to drive a manual transmission – a little clunky at first, but once you get the hang of the clutch and revs, you’re cruising.
Core vs. Extension: What You’re Actually Using
Out of the box MediaWiki ships with a handful of builtin functions – #if, #expr, #time, and friends. The ParserFunctions extension expands that toolbox dramatically, adding #switch, #eq, #var and a suite of #vardefine utilities. Most modern wikis enable the extension; if you’re not sure, peek at LocalSettings.php for a line that reads wfLoadExtension( 'ParserFunctions' );. In short: you’re probably already standing on a richer platform than you think.
Quick sanity check
{{#if:{{PAGENAME}}|Page name is {{PAGENAME}}|No name?}}If that spits out “Page name is …” you’ve got the basics working. If not, you might have a caching quirk or the extension isn’t enabled – but that’s a story for another day.
Conditional Rendering – The #if Family
#if is the Swiss‑army knife of parser functions. It evaluates a string and decides which branch to show. The syntax is forgiving – any non‑empty value counts as “true”. That means you can feed it a template parameter, a magic word, or even the result of another function.
{{#if:{{{status|}}}
| {{#if:{{{status}}}|Status: {{{status}}}|No status provided}}
| {{#ifexist:{{FULLPAGENAME}}|Page exists|Missing page}}}Notice how the inner #if re‑checks the same variable. It looks redundant – and it is – but that redundancy can be a safety‑net when dealing with user‑generated content that might be stripped or trimmed elsewhere.
#switch for multi‑branch logic
When you have more than two possible outcomes, nesting #if quickly turns ugly. Enter #switch – basically a tidy case statement. It’s perfect for handling status flags, language codes, or even custom taxonomy.
{{#switch:{{{type|default}}}
| article = {{#ifexist:Template:Article|{{Template:Article}}}}
| tutorial = {{#ifexist:Template:Tutorial|{{Template:Tutorial}}}}
| reference = {{#ifexist:Template:Reference|{{Template:Reference}}}}
| #default = {{#ifexist:Template:Default|{{Template:Default}}}}Here, if someone passes type=article to a template, the corresponding sub‑template is pulled. If the key is missing, the #default branch catches the stray case. It’s a clean, readable way to avoid a tangled forest of #ifs.
Arithmetic and Logic – #expr
When you need to compute dates, scores, or any numeric result, #expr is your friend. It supports the usual operators (+, -, *, /, %), comparison (<, >, ==, !=) and even logical operators (and, or, not). The syntax can look a little cramped, so many authors wrap it in a variable definition for clarity.
{{#vardefine:age|{{#expr:{{#time:Y}} - {{{birth_year|1900}}}}}}That snippet calculates a person’s age from their birth year. You can then reuse {{#var:age}} later in the same template or even in other templates that import it. A small neat trick: add a round call if you ever need to truncate decimals.
Using #expr for conditional CSS classes
Styling based on a value? Sure thing.
<div class="{{#ifexpr:{{#var:age}} >= 65 | senior | adult}}">
{{#var:age}} years old.
</div>Here the #ifexpr (a variant of #expr with an if‑like output) decides whether the senior or adult class is applied, letting CSS do the heavy lifting.
Time‑Sensitive Content – #time and Friends
Dynamic dates are a classic use‑case. #time formats timestamps using PHP’s date() syntax, so you can render “today”, “yesterday”, or even “the first Monday of next month”.
{{#time:F j, Y}} Combine that with #expr to calculate relative periods.
{{#expr: {{#time:U}} - (7*24*60*60)}}The above yields the Unix timestamp one week ago. Wrap it in #time again to show a readable date.
Cache control for time‑based pages
If a page shows “Last updated: {{#time:H:i}}”, you probably want the server to refresh it each minute. MediaWiki caches pages aggressively, but you can hint at freshness with __NOINDEX__ (preventing search indexing) or by sprinkling __STATIC__ as a marker for extensions that respect it. A more surgical method is to embed a #invoke Lua module that returns mw.getCurrentTimestamp() – Lua runs at render time, bypassing the static cache.
Variables – #vardefine, #var, #var_exists
Variables are the unsung heroes that let you store intermediate results without polluting the page’s visible output. The three‑step dance looks like this:
- Define:
{{#vardefine:myVar|value}} - Use:
{{#var:myVar}} - Check:
{{#ifexist:{{#var:myVar}}|exists|missing}}
When you chain variables, be mindful of scope. By default, a variable lives for the duration of the current parser call – that means it won’t survive a #invoke Lua call unless you pass it explicitly.
Recursive templates with variables
Want to walk a hierarchical list (say, a category tree)? You can do a shallow recursion using #var to keep track of depth.
{{#vardefine:depth|{{#expr:{{#var:depth|0}}+1}}}}
{{#if:{{#var:depth}}<=5 |
* [[Category:Level {{#var:depth}}]]
{{#subpagename}}
}}The guard <=5 prevents infinite loops – a safe‑guard every human coder learns the hard way.
String Manipulation – #urlencode, #explode, #replace
While MediaWiki isn’t a full‑blown scripting language, it does give you enough tools to tinker with strings. For instance, #urlencode ensures that a user‑provided value fits safely into a query string.
[https://example.com/search?query={{#urlencode:{{{search|}}}}} Search results]If you need to split a comma‑separated list, #explode turns it into a sequence you can loop over with #foreach (provided you’ve enabled the ParserFunctions extension with the “foreach” feature). Here’s a quick demo:
{{#foreach:{{#explode:{{{tags|}}}|,}} | tag |
{{tag}}
}}Notice the subtle whitespace handling – MediaWiki trims a lot but not everything. Adding a #trim around {{tag}} can clean up stray spaces.
Interfacing with Semantic MediaWiki
If your wiki runs Semantic MediaWiki (SMW), parser functions become bridges to semantic queries. You can inject a property value into a template, then decide what to display based on its presence.
{{#ask: [[Category:Project]]
| ?ProjectStatus
| format=template
| template=ProjectRow
| link=none}}Inside Template:ProjectRow, you might have:
{{#if:{{{ProjectStatus|}}}
| {{#switch:{{{ProjectStatus}}}
| Active = Active
| Completed = Completed
| #default = Unknown}}
| No status recorded}The combination of SMW’s query engine and parser functions yields a live dashboard that updates as soon as someone edits a single property. No JavaScript required, just pure wikitext.
Performance Tips – Keep the Parser Happy
Every parser function adds a tiny amount of overhead. On a small wiki, you won’t notice, but on a massive installation the tiny costs multiply. Here are three low‑tech habits that keep things snappy:
- Cache‑friendly output: Use
__STATIC__or__NOINDEX__judiciously, but don’t blanket‑apply them. The parser can still reuse fragments that don’t change. - Avoid deep recursion: If you find yourself nesting more than three
#if/#switchlayers, consider pulling that logic into a Lua module. Lua runs faster for heavy loops. - Minimize string concatenation: Each
#replaceor#explodecreates a new temporary string. Group related operations in a single function when possible.
Pro tip: the official parser function manual lists a “caching hint” parameter for some functions (nocache) – use it only when the result truly varies per request.
Putting It All Together – A Mini Dashboard Example
Below is a compact template that pulls a page’s last edit time, calculates its age in days, flags overdue pages, and displays a coloured badge. It showcases #time, #expr, #ifexpr, variables and conditional CSS.
{{#vardefine:lastEdit|{{#time:U|{{#property:Revision timestamp}}}}}}
{{#vardefine:ageDays|{{#expr: ({{#time:U}} - {{#var:lastEdit}}) / 86400}}}}
{{#ifexpr:{{#var:ageDays}} > 30 |
Over 30 days
| Updated {{#time:F j, Y|{{#var:lastEdit}}}}}}
In a real wiki you’d replace {{#property:Revision timestamp}} with an SMW property or a Lua call to mw.title.getCurrentRevision().timestamp. The point is that the same pattern repeats across countless pages – a single line of wikitext can drive a full‑featured status board.
Final Thoughts
Parser functions are the unsung workhorse of MediaWiki. They’re not flashy, but they let you embed logic directly where the content lives, keeping your site’s behavior transparent and easy to audit. The biggest hurdle is the learning curve – the syntax feels quirky, the documentation is terse, and the occasional caching surprise can be frustrating. Yet, once you get past the initial “what‑does‑that‑pipe‑do?” moment, you’ll find yourself reaching for #if and #expr before you even think about writing a line of JavaScript.
So next time you need a piece of text that changes based on a user’s input, a date, or a semantic property, remember the parser function toolbox is already in your wiki’s back pocket. A few well‑placed functions can turn a static page into a living component that updates itself, reflects the latest data, and does it all without leaving the comforting realm of plain wikitext.