Several years ago I wrote about multi-instance subtemplates and how to best handle them in Lua. There’s also a related pattern of multi-instance templates which are not subtemplates:
{{Sample/start}}
{{Sample|image= |author= }}
{{Sample|image= |author= }}
{{Sample|image= |author= }}
{{Sample/end}}
In this case we’ll print some opening block of HTML (div with a class, table with header columns, etc), print several entries, and then close out the block. This is a pretty widely used pattern and doesn’t have nearly as many UX considerations as the case of multi-instance subtemplates does, so I had not ever given much thought to improving it1 until very recently when a chainmaille wiki contributor asked me if there was any way I could possibly remove the need for the /start and /end templates.
And then…well…I did.2

Important caveat
These techniques ONLY work if you are certain to have only a single instance of this section on any given page. This is often the case when you’re using this pattern, but not always.
Removing the /start template
The first part, removing the /start template, is trivial as long as you have Variables installed. At the start of Template:Sample, put something like:
{{#if:{{#var:SAMPLE_SECTION_STARTED}}
|<!-- do nothing, otherwise -->
|<div class="sample-section">{{#vardefine:SAMPLE_SECTION_STARTED|true}}
}}
If we’ve already started the sample section, we do nothing; if we haven’t then we open the section and log that we’ve started. That vardefine could go outside the #if statement, it’s not a meaningful performance improvement to keep it inside if that feels confusing.
Just remember to keep track of your whitespace in between the #if statement and your next block of code.
A bit about var_final
Before we can talk about how to remove the need for the /end template, I want to introduce a bit about #var_final. If you want to, you can also check out its source code directly, but that’s not required.
Basic functionality
Let’s say we have this block of code on a page:
{{#vardefine:x|value 1}}
{{#var_final:x}}
{{#vardefine:x|value 2}}
Then we’ll print value 2. The most straightforward definition of #var_final is that it outputs the final value a variable holds when the page is rendered.
Behavior inside of functions
If we try to put #var_final inside of a function, we’ll get some weird behavior. For example, if you try to #replace: or #len: a #var_final call you’ll just get an empty string (try it!). But if we try the following:
{{#vardefine:x|1}}
{{#vardefine:y|{{#var_final:x}}}}
{{#vardefine:x|2}}
{{#var:y}}
Then we’ll correctly print a 2. And the line {{#if:true|{{#var_final:x}}}} added anywhere to that block of code will also print a 2. Try these things, too!
Strip markers
The issue is that #var_final temporarily prints a strip marker during template expansion, which is then replaced with the final variable of the variable at the end of parsing. If we try to evaluate based on the value of a strip marker, weird things will happen; but strip markers can be results of functions and get processed correctly.
We can peer into this by writing a couple lines of Lua code:
|
|
If we run this in the Lua console, we can see:

This might look familiar to you if you’ve ever tried to store a <nowiki/> inside of Cargo.
A bit about expanding variable names
Since MediaWiki code is parsed starting out as one long string, we can do some things that would absolutely not be allowed in other programming languages. For example, if we want to call a template based on a user parameter, this is totally allowed:
{{ {{{custom_formatter|ItemFormatter}}}| {{{1|}}} | {{{2|}}} | {{{3|}}} }}
If we specify the parameter |custom_formatter= then we’ll pass parameters 1, 2, and 3 to that template; otherwise, we’ll feed them into Template:ItemFormatter.3
We can also do the same thing with variables, for example:
{{ {{#var:formatter}}| {{{1|}}} | {{{2|}}} | {{{3|}}} }}
This template will do the same thing, except based on the value of {{#var:formatter}} rather than the custom_formatter parameter.
And this also works with variable names. For example, we can do this:
{{#var:{{{attribute|}}}_{{{name|}}} }}
In this example, if we supply |name=Apple |attribute=rarity then we’ll pull out the variable {{#var:rarity_Apple}}. This kind of thing can be horribly abused in a lot of ways to do array-like or even json-like static data in wikitext in a horrible, scary, confusing, inefficient way. Please don’t do that.
But occasionally this can be useful.
Template:Iter
I have a template along these lines on most wikis I edit:
{{#vardefine:varname|ITER_INDEX_{{#if:{{{1|}}}|{{{1}}}|default}}<!-- end vardefine -->}}<!--
main logic
-->{{#vardefineecho:{{#var:varname}}|
{{#expr:
{{#ifeq:{{lc:{{{reset|}}}<!-- end lc -->}}
|yes
|0 <!-- start from 0 if resetting -->
|{{#var:{{#var:varname}}|0}}
<!-- end ifeq -->}} + {{#if:{{{get|}}}|0|1}} <!-- add 0 instead of 1 if we're getting -->
<!-- end expr -->}}
<!-- end vde -->}}
This template creates an iterator that counts up each time it’s called. Here’s how it works:
- Let’s iterate the variable
ITER_INDEX_defaultorITER_INDEX_{{{1}}}depending on whether the user provides a key to the iterator. This way we can have multiple independent iterators on the same page. - Because that expression is pretty long, store the key of the variable in
{{#var:varname}}and use that to access throughout this template - If we are told to
|reset=yes, then start over and return0+1 - If we are told to
|get=yes, then just return the previous value, which is the current value + 0 - Else, return the previous value + 1 and remember the current value for next time
I find this template incredibly useful when I want sortable tables that need an auto-increment column.
Removing the /end template
To remove the need for the /end template, we have to close out our div or table when there are no further instances of our template. We will do this using #var_final, and here are the requirements:
- We can’t compare the final value of a variable against anything, so something like
{{#ifeq:{{#var_final:x}}|{{#var_x}}|</div>}}is not allowed - We also can’t manipulate the final value in some way, so like
{{#replace:{{#var_final:x}}|true|</div>}}is not allowed
Add var_final support to Template:Iter
The version of Template:Iter on maille.wiki has a bit of additional code that also supports returning the #var_final of a variable. I’ll leave it as an exercise to the reader to make sure you understand this (more or less):
<includeonly>{{#vardefine:varname|ITER_INDEX_{{#if:{{{1|}}}|{{{1}}}|default}} }}<!--
-->{{#if:{{{final|}}}<!--
just return the var_final and do nothing more
-->|{{#var_final: {{#var:varname}} }}<!--
else do the whole increment thing
-->|{{#vardefineecho:{{#var:varname}}|<!--
-->{{#expr:<!--
always evaluate an expr but if we're doing "reset" then start from 0 (rather than the var); and if we're doing "get" then add 0 (rather than 1)
-->{{#ifeq:{{lc:{{{reset|}}}}}|yes|0|{{#var:{{#var:varname}}|0}}}}<!--
-->+<!--
-->{{#if:{{{get|}}}|0|1}}<!--
-->}}<!--
<!-- /vde -->}}
<!-- /if final -->}}</includeonly><noinclude>{{documentation}}[[Category:Formatting templates]]</noinclude>
Solution: Creating an “array” of possible final variables
We’ll be looking at an “array” of variables with names like Sample_close_div_{{Iter|sample}}, so we’ll have {{#var:Sample_close_div_1}}, {{#var:Sample_close_div_2}}, etc. The array looks like this:
- Every variable is initialized with a value of
</div> - In the subsequent template (if there is one), that value is overwritten with an empty string
Then, at the end of our template all we do is print the #var_final of that particular element of the “array” and it will only do something if we are in fact in the last one.
It looks something like this at Template:Sample:
{{#vardefine:Sample_close_div_{{Iter|sample|get=yes}}|}}<!--
-->{{#vardefine:Sample_close_div_{{Iter|sample}}|</div>}}<!--
template content goes here
-->{{#var_final:Sample_close_div_{{Iter|sample|get=yes}}}}
Putting it all together
We do need to take care of both the /start and the /end templates at the same time, so we need to tweak the code above slightly. Here’s how the code at Template:Sample looks:
<!--
Don't close out the section in the previous one if it wasn't the last one
-->{{#vardefine:Sample_close_div_{{Iter|sample|get=yes}}|}}<!--
Open the section if this is the first
-->{{#ifeq:{{Iter|sample}}|1|<div style="display:flex; align-items:top;">}}<!--
Prepare to close out the section at the end if this turns out to be the last one
-->{{#vardefine:Sample_close_div_{{Iter|sample|get=yes}}|</div>}}<!--
Start template content
--><!--
Close out the section if this is the last one
-->{{#var_final:Sample_close_div_{{Iter|sample|get=yes}}}}
And that’s it!
When is this actually a good idea?

Honestly, this could have been a lot worse. I’d say you’re probably fine doing this as long as:
- You understand what’s going on in this code and could debug an issue with it
- Your end template isn’t “expensive” in any way - certainly not having any expensive parser functions, but even if it’s just a lot of bytes you may not want to do this, as you will actually parse the value once per use of the item template.
- Someone has actually complained about the
/startand/endtemplates being there. If not, you should probably just do the easy approach.
-
Other than to debate whether you should say
Samples/startorSample/startas the template name. Like, it’s nice if the start/end templates are subpages of the data template, but it’s also the start of the samples plural, not just a single sample, so…. ↩︎ -
Alex helped! Thanks Alex!!!! ↩︎
-
Though I used the
{{{param|fallback}}}syntax for readability here, generally I don’t recommend that ↩︎