Mastering MediaWiki's Template System for Advanced Customization

Why Templates Aren’t Just Boilerplate

Ever stared at a page full of {{Infobox}} tags and thought, “Is there a way to make this dance to my own tune?” Spoiler: absolutely. Templates in MediaWiki are like a Swiss‑army knife—cut, twist, and even shave a bit of code while you’re at it. The trick is not just slapping a {{Template}} on a page, but coaxing it into doing something truly bespoke.

When MediaWiki 1.41 dropped the “TemplateData” enhancements (April 2024, if you’ve been keeping tabs), it felt like someone finally handed us a more ergonomic screwdriver. Suddenly we could describe parameters in a way that the visual editor actually understood. That’s the sort of change that makes you sit up, coffee in hand, and whisper “this could be useful.”

Getting Comfortable with the Basics—And Then Some

First, let’s not reinvent the wheel. A vanilla template looks like this:


{{#if:{{{title|}}}|'''{{{title}}}'''|''No title provided''}}

It checks if the title parameter is set; if not, it shows a fallback. Simple, right? But here’s where most newbies stop, and where the magic begins.

Imagine you need a template that not only shows a title but also pulls in the latest revision timestamp, formats it, and adds a little “last edited” badge. That’s a handful of wikitext gymnastics, but it’s doable.

Step‑One: The #invoke Power‑Move

Lua modules are the secret sauce for heavy lifting. You can think of them as the “engine room” while the template is just the control panel.


{{#invoke:Display|showTitle|title={{{title|Untitled}}}}}

And in Module:Display you’d have something like:


local p = {}
function p.showTitle(frame)
    local title = frame.args.title or "Untitled"
    local rev = mw.title.getCurrentTitle().latestRevisionId
    local ts = mw.site.timestampToString(mw.title.getCurrentTitle().revisionTimestamp)
    return string.format("'''%s''' rev %s (%s)", title, rev, ts)
end
return p

Notice the little quirks: we grab the revision ID directly from the title object. If you’ve never dabbled in Lua on a wiki, the syntax looks a bit odd at first—like trying to read a recipe written in a different language. But once you get the hang of mw library calls, it’s as satisfying as finding the perfect shortcut on a road trip.

Step‑Two: Parameter Defaults and Validation—A Bit of Guard‑Rail

One of the subtle pitfalls is letting users feed in unexpected values. With the new TemplateData (thanks again, 1.41), you can declare types and default values:


{{#templatedata:
{
  "params": {
    "title": {
      "label": "Page title",
      "type": "string",
      "required": false,
      "default": "Untitled"
    },
    "showBadge": {
      "label": "Show edit badge",
      "type": "boolean",
      "default": true
    }
  }
}
}}

Now the visual editor will offer a neat little UI, and any stray “yes” or “nope” that a careless editor might type gets caught early. It’s a tiny UX win that feels surprisingly polished.

Step‑Three: Nesting Templates—Like Russian Dolls

Here’s where we start to get a little wild. Suppose you have a generic {{InfoBox}} that you want to sprinkle on various article types: biographies, events, tech specs. Instead of making ten separate templates, you can embed a “layout” template inside a “content” template.


{{InfoBox
|title = {{#if:{{{person|}}}|{{{person}}}|Unknown}}
|image = {{{image|}}}
|details = {{InfoboxDetails|data={{{details|}}}}}
}}

What’s happening? The outer InfoBox defines the skeleton, while InfoboxDetails takes a blob of data (maybe JSON‑like key/value pairs) and renders rows accordingly. It’s a pattern that keeps your markup DRY (don’t repeat yourself) without turning everything into a monolith.

Pro tip: keep an eye on the #iferror parser function when nesting—if something downstream throws a tantrum, the whole box can collapse. A quick #iferror:{{{...}}}|fallback saves you from a broken page.

Advanced Customization: Conditional Styling with #if and CSS Classes

Ever wanted a template that changes its colour based on a parameter? Sure thing. You can pass a class name and let your site’s CSS handle the rest.


{{#if:{{{alert|}}}
 |{{{alert}}}
 |
}}

Now, on the CSS side you might have:


.alert.info { background:#e0f7fa; border-left:4px solid #00bcd4; }
.alert.warning { background:#fff3e0; border-left:4px solid #ff9800; }
.alert.error { background:#ffebee; border-left:4px solid #f44336; }

That little #if trick lets you set the class to warning, error, or any custom value—no need to duplicate the HTML for each style.

Internationalisation (i18n) – Making Templates Speak Many Languages

Because the world isn’t monolingual, you’ll eventually need your template to output text in the reader’s language. MediaWiki’s message system (the Message class in Lua or {{int:}} in wikitext) is your friend.


{{#invoke:Display|showBadge|msg=edit-badge}}

And in Module:Display:


local i18n = mw.message:new()
function p.showBadge(frame)
    local msgKey = frame.args.msg or "default-badge"
    local badge = i18n:plain(msgKey)
    return string.format("<span class='badge'>%s</span>", badge)
end

The edit-badge key would be defined in i18n/en.json, i18n/de.json, etc. When a user switches language, the badge text follows suit automatically. It feels like a tiny miracle each time you see “Letztes Update” appear for German readers.

Performance Considerations – Don’t Let Templates Slow You Down

It’s easy to get carried away and pile a mountain of #invoke calls on a single page. The server will do the math, but you’ll notice lag if you’re not careful. A few best practices:

  • Cache heavy‑lifting results with #cached or Lua’s mw.cache API.
  • Limit the depth of nested templates—five levels is usually safe; ten, you might be asking for trouble.
  • Prefer {{#if}} over #iferror in hot loops; the former short‑circuits earlier.

My own wiki runs a “RecentChangesMini” template that pulls the last ten edits. I wrapped the data fetch in a cache block that lives for 30 seconds. The difference? The page loads in under a second instead of three. That’s the sort of micro‑optimisation that keeps editors happy.

A Real‑World Walkthrough – Building a “Project Status” Box

Okay, enough theory. Let’s build something you could drop into a community portal. The goal: a box that shows a project’s name, lead, current phase, and a progress bar that updates based on a percent parameter.

First, the template skeleton (Template:ProjectStatus):


{{#if:{{{name|}}}
 |<div class="proj-status {{#if:{{{phase|}}}|{{{phase}}}|unknown}}">
   <h3>{{{name}}}</h3>
   <p>Lead: {{{lead|Unassigned}}}</p>
   {{#if:{{{percent|}}}
     |{{#invoke:ProgressBar|render|percent={{{percent}}}}}
     |<!-- no progress bar -->
   }}
 </div>
 |
}}

Now the Lua module (Module:ProgressBar) that spits out a simple HTML bar:


local p = {}
function p.render(frame)
    local pct = tonumber(frame.args.percent) or 0
    if pct < 0 then pct = 0 elseif pct > 100 then pct = 100 end
    local bar = string.format(
        '<div class="progress">'..
        '<div class="progress-fill" style="width:%d%%;"></div>'..
        '<span class="progress-label">%d%%</span>'..
        '</div>', pct, pct)
    return bar
end
return p

And a tiny CSS snippet to make it look decent:


.progress { background:#f0f0f0; border-radius:4px; overflow:hidden; position:relative; height:1.5em; }
.progress-fill { background:#4caf50; height:100%; transition:width .3s ease; }
.progress-label { position:absolute; left:50%; top:0; transform:translateX(-50%); font-size:.9em; color:#fff; }
.proj-status.unknown h3 { color:#999; }

Drop those three pieces onto your wiki, and you have a reusable component. Editors can now add:


{{ProjectStatus
|name=WikiDocs Revamp
|lead=Alice
|phase=beta
|percent=68
}}

and instantly see a nicely coloured box with a bar that slides to 68 %. If the percent param is omitted, the bar simply disappears—no ugly empty space.

Debugging Tips – When Templates Throw a Fit

Templates can be temperamental, especially when you start mixing wikitext, Lua, and CSS. Here are a few habits I’ve picked up:

  1. Use {{#showdebug}}: It prints out the parameter values so you can see what the parser is actually receiving.
  2. Inspect the HTML source: Right‑click → “View page source” and hunt for stray <!-- comments that may be breaking your layout.
  3. Check the Lua sandbox logs: Append mw.log('debug', variable) inside your module; the messages appear in the wiki’s error log.
  4. Don’t forget about caching side‑effects: If a change isn’t showing up, you might need to purge the page (?action=purge) or clear the module cache.

Lastly, remember that the parser is forgiving but not omniscient. A missing pipe or stray brace can throw the whole thing off, and you’ll get a “Template loop error” that looks scarier than it is. A quick #if check around risky sections can save you from an infinite recursion nightmare.

Future‑Proofing – Keeping Up with MediaWiki Changes

MediaWiki evolves. The #templatestyle parser function landed in 1.42, letting you inject CSS directly from a template without touching the site’s stylesheet. That opens the door for per‑template theming—think of a {{AlertBox}} that can change colour based on a theme param on the fly.

Another upcoming feature is the “TemplateData 2.0” schema, which promises richer UI controls (sliders for numeric params, colour pickers, etc.). When that ships, you’ll be able to define a percent param as a slider ranging from 0–100, making the editing experience slicker than ever.

My advice? Keep an eye on the release notes and the “Template” tag on the MediaWiki bug tracker. When a new hook appears, try to sandbox it in a test wiki before rolling it out to production.

Wrapping Thoughts—No, Really, Not a Wrap‑Up

So, what have we covered? A quick tour from basic {{if}} checks to Lua‑powered progress bars, plus a dash of i18n, caching, and future‑proofing. The takeaway is simple: templates are not static placeholders; they’re dynamic, programmable pieces of your wiki’s UI. Treat them like mini‑apps, give them proper parameters, test them, and don’t be afraid to sprinkle a little humor or odd phrasing here and there—after all, a wiki is a community, not a factory.

If you find yourself tweaking the same snippet across dozens of pages, pause. That’s a sign you’ve stumbled onto a reusable pattern. Wrap it in a template, add a few #if guards, maybe a Lua module for the heavy lifting, and watch the maintenance load shrink. And the next time you encounter a new MediaWiki release, think of the fresh toolbox it brings—maybe a new parser function or a better way to declare TemplateData. Dive in, experiment, and let the wiki evolve alongside its editors.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe