MediaWiki Scribunto & Lua — Lua Modules, Scribunto Scripting, and Dynamic Templates
In this tutorial, you will learn about MediaWiki Scribunto & Lua. We cover key concepts, practical examples, and best practices to help you master this topic.
Scribunto and Lua in MediaWiki let you write programmable templates using the Lua scripting language — creating modules that handle complex logic, string manipulation, data processing, and dynamic content generation beyond what wikitext parser functions can do, just as Wikipedia uses Lua for its most sophisticated infoboxes and navigation systems.
What You'll Learn
- Installing the Scribunto extension
- Creating Lua modules in the Module namespace
- Writing functions that accept template parameters
- Using Lua libraries for string, math, and table operations
- Calling Lua modules from wiki templates
- Debugging Lua modules
Why It Matters
Parser functions (#if, #switch, #expr) handle basic logic, but they become unreadable and slow with complex operations. Lua is a real programming language. It handles loops, data structures, file operations, and complex string processing efficiently. A single Lua module can replace 50 lines of nested parser functions with 10 lines of clean Lua code. Scribunto modules load faster, are easier to debug, and produce more maintainable templates.
Real-World Use
A DodaTech wiki uses a Lua module to format version numbers. The module accepts "210" and outputs "2.1.0", accepts "31415" and outputs "3.14.15", and handles special cases like "100" becoming "1.0.0". Another module maintains a lookup table of product codenames. A third module generates breadcrumb navigation from the page title hierarchy. All three would be impractical with parser functions alone.
Learning Path
flowchart LR A["26: Semantic MediaWiki"] --> B["27: VisualEditor"] B --> C["28: Scribunto & Lua"] C:::current D["29: Cite & References"] E["30: Interwiki Links"] F["31: Wiki Farms"] C --> D --> E --> F classDef current fill#38bdf8,color#0f172a,stroke-width:2px
Step 1: Install Scribunto
cd /opt/lampp/htdocs/mediawiki/extensions
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto.git
cd Scribunto
git checkout REL1_42
Enable in LocalSettings.php:
wfLoadExtension( 'Scribunto' );
Scribunto has no configuration options. It works immediately after enabling. Verify it appears on Special:Version.
Lua Binary
Scribunto uses the Lua language. It requires a Lua Interpreter:
# Install Lua
apt install lua5.1
# Or compile from source
wget https://www.lua.org/ftp/lua-5.1.5.tar.gz
tar -xzf lua-5.1.5.tar.gz
cd lua-5.1.5
make linux
make install
Scribunto also includes a sandboxed PHP-based Lua interpreter as fallback.
Step 2: Understand the Module Namespace
Lua modules live in the Module: namespace. Each module is a Lua script that returns a table of functions.
Module Structure
= Module:Hello =
local p = {}
function p.hello( frame )
return "Hello, world!"
end
function p.greet( frame )
local name = frame.args[1] or "World"
return "Hello, " .. name .. "!"
end
return p
Key points:
pis a table that holds the module's functions- Each function receives a
frameobject with parameter access frame.args[1]accesses the first unnamed parameterframe.args["name"]accesses a named parameter- The module must return the table
p
Step 3: Call Lua from Templates
Create a template that invokes the Lua module:
= Template:Hello =
{{#invoke:Hello|hello}}
= Template:Greet =
{{#invoke:Hello|greet|{{{1|World}}}}}
Usage on a page:
{{Hello}} Renders: Hello, world!
{{Greet|Alice}} Renders: Hello, Alice!
{{Greet}} Renders: Hello, World!
The #invoke parser function calls a Lua module. The syntax is {{#invoke:ModuleName|FunctionName|param1|param2|...}}.
Step 4: Access Wiki Data from Lua
Lua modules can access wiki pages, categories, and templates.
Getting Page Content
local p = {}
function p.getPageContent( frame )
local title = frame.args[1] or "Main Page"
local page = mw.title.new( title )
if page.exists then
return "Title: " .. page.fullText
else
return "Page '" .. title .. "' does not exist."
end
end
function p.getPageText( frame )
local title = frame.args[1] or "Main Page"
local page = mw.title.new( title )
if page.exists then
return page:getContent()
else
return ""
end
end
return p
Site Information
local p = {}
function p.siteInfo( frame )
local siteName = mw.site.siteName
local currentTime = os.date( "%Y-%m-%d %H:%M:%S" )
local pageCount = mw.site.stats.pagesInNamespace( 0 )
local userCount = mw.site.stats.numberUsers
return string.format(
"%s — %s — %d pages — %d users",
siteName, currentTime, pageCount, userCount
)
end
return p
Step 5: String and Math Operations
Lua's string library is more powerful than wikitext parser functions.
String Formatting
local p = {}
function p.formatVersion( frame )
local version = frame.args[1] or "100"
-- Convert "210" to "2.1.0"
local parts = {}
for i = 1, #version do
table.insert( parts, version:sub( i, i ) )
end
return table.concat( parts, "." )
end
function p.truncate( frame )
local text = frame.args[1] or ""
local maxLen = tonumber( frame.args[2] ) or 100
if #text > maxLen then
return text:sub( 1, maxLen ) .. "..."
end
return text
end
return p
Mathematical Operations
local p = {}
function p.fileSize( frame )
local bytes = tonumber( frame.args[1] ) or 0
local units = { "B", "KB", "MB", "GB", "TB" }
local unitIndex = 1
while bytes >= 1024 and unitIndex < #units do
bytes = bytes / 1024
unitIndex = unitIndex + 1
end
return string.format( "%.1f %s", bytes, units[unitIndex] )
end
function p.percentage( frame )
local value = tonumber( frame.args[1] ) or 0
local total = tonumber( frame.args[2] ) or 1
if total == 0 then return "0%" end
local percent = ( value / total ) * 100
return string.format( "%.1f%%", percent )
end
return p
Step 6: Working with Tables
Tables are Lua's primary data structure. Use them for lookup tables and data processing.
Lookup Table
local p = {}
local productData = {
["DodaBrowser"] = { version = "5.2", released = "2026-03-15", status = "Stable" },
["DodaSync"] = { version = "2.1", released = "2026-01-20", status = "Stable" },
["DodaZIP"] = { version = "3.0", released = "2025-11-01", status = "Beta" },
["Durga AV"] = { version = "4.8", released = "2026-06-01", status = "Stable" },
}
function p.getProductInfo( frame )
local product = frame.args[1] or ""
local info = productData[product]
if not info then
return "Product '" .. product .. "' not found."
end
return string.format(
"%s: v%s (%s) — %s",
product, info.version, info.released, info.status
)
end
function p.listAllProducts( frame )
local results = {}
for name, data in pairs( productData ) do
table.insert( results, string.format(
"* %s — v%s (%s)",
name, data.version, data.status
) )
end
table.sort( results )
return table.concat( results, "\n" )
end
return p
Step 7: Debugging Lua Modules
Print Debug Output
Add temporary debug output:
function p.debug( frame )
local params = {}
for key, value in pairs( frame.args ) do
table.insert( params, key .. " = " .. value )
end
return "Parameters received:\n" .. table.concat( params, "\n" )
end
Using mw.log
The mw.log() function writes to the debug log:
function p.test( frame )
mw.log( "Test function called" )
mw.logObject( frame.args, "Args" )
return "Check the debug log"
end
View the log in Special:Log/scribunto.
Common Errors
Lua error: attempt to index a nil value
→ You tried to access something that doesn't exist (e.g., frame.args[1] when no parameter was provided)
Lua error: attempt to call a nil value (method '...')
→ You misspelled a function name or the library is not available
Lua error: loop or previous error loading module
→ Your module has a syntax error. Check for missing 'end' or 'then'.
Step 8: Lua Module Best Practices
Keep Modules Focused
One module per functional area. Do not put all functions in one massive module.
Use Local Functions
Keep helper functions private (local):
local function sanitize( text )
return text:gsub( "%s+", " " ):gsub( "^%s*(.-)%s*$", "%1" )
end
function p.safeGreet( frame )
local name = sanitize( frame.args[1] or "World" )
return "Hello, " .. name .. "!"
end
Cache Expensive Operations
Store results of expensive operations:
local siteName = nil
function p.getSiteName( frame )
if not siteName then
siteName = mw.site.siteName
end
return siteName
end
Use the mw.html Library
Build structured HTML output safely:
function p.buildInfobox( frame )
local div = mw.html.create( "div" )
:addClass( "infobox" )
:css( "border", "1px solid #ccc" )
:css( "padding", "10px" )
:wikitext( frame.args[1] or "" )
return tostring( div )
end
What You Learned
- Scribunto enables Lua scripting for MediaWiki templates
- Modules are stored in the
Module:namespace {{#invoke:Module|Function|params}}calls Lua from wiki pagesframe.argsprovides access to template parameters- Lua libraries handle string, math, table, and HTML operations
mw.titleandmw.siteaccess wiki data- Debugging uses
mw.log()and the scribunto log
In the next lesson, you'll learn about citations and references.
Common Mistakes
| Mistake | Why It Happens | How to Fix |
|---|---|---|
| "Lua error: Script error" | Syntax error in Lua code | Check for missing end, then, or function keywords. Use mw.log() to trace execution. |
| Module not found error | Wrong module name in #invoke |
Verify the module page exists at Module:Name. The name is case-sensitive. Check for typos. |
| Parameters not received | Wrong frame.args access | Parameters from the template call go to frame.args. Named parameters use frame.args["name"]. Positional parameters use frame.args[1], frame.args[2], etc. |
| Lua is slower than wikitext | Inefficient Lua code | Avoid global variables. Cache results of mw.title.new(). Use string manipulation efficiently. |
| Security warning in Lua module | Unsafe operations detected | Scribunto sandboxes Lua for security. You cannot access the filesystem, network, or execute shell commands. Use wiki-specific APIs instead. |
Practice Questions
- How do you call a Lua module function from a wiki page, and how are parameters passed?
- Write a Lua module that takes a birth date and calculates the person's age in years.
- What is the purpose of the
frameobject in Scribunto functions? - Challenge: Build a complete product information system with Lua. Create a module called
ProductDBwith a lookup table of 5 DodaTech products (name, version, release date, description). Write functions that: (a) display a single product's details, (b) list all products in a formatted table, (c) filter products by status (Stable/Beta/Alpha), (d) format version numbers consistently. Create template wrappers for each function so wiki editors can use them with simple{{ProductInfo|ProductName}}syntax. Create a "Products" page that uses the module to display all products in a dynamic table.
FAQ
Mini Project
Goal: Build a complete Lua-powered template system for a product wiki.
- Create a Lua module
ProductDatawith a lookup table of 5 products - Create a function that returns a formatted product card with HTML
- Create a function that lists all products in a sortable table
- Create a function that searches products by keyword
- Create template wrappers for each function
- Add input validation (handle missing parameters, invalid products)
- Add a debug function that shows all received parameters
- Create a test page that uses all functions
- Compare the Lua module's performance against an equivalent parser-function implementation
What's Next
Lua scripts make your templates powerful. Now let's add proper citations and references for credible documentation.
Continue to Lesson 29: Cite & References — learn about footnotes, citation templates, and the CiteThisPage extension.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro