Conditionals, loops and variables
XSLT has no assignment. Every value is bound once, and that single constraint explains most of how stylesheets are structured.
if, choose and for-each
<xsl:choose>
<xsl:when test="@status = 'published' and published-date <= current-date()">
<span class="live">Live</span>
</xsl:when>
<xsl:when test="@status = 'draft'">
<span class="draft">Draft</span>
</xsl:when>
<xsl:otherwise>
<span class="unknown">Unknown</span>
</xsl:otherwise>
</xsl:choose>
<xsl:for-each select="item">
<li>
<xsl:value-of select="position()"/>.
<xsl:value-of select="title"/>
</li>
</xsl:for-each>xsl:ifhas no else branch; usexsl:choosewhen you need one.current-date()is XSLT 2.0. In 1.0 you pass the date in as a parameter from the calling program.- Inside
for-eachthe context changes, soapply-templatesandvalue-ofare relative to the item. - Prefer
apply-templatesoverfor-eachwhen the nodes have their own template — the loop version duplicates logic that belongs in a template.
Variables are bindings, not slots
<xsl:variable name="items" select="//item"/>
<xsl:variable name="total" select="sum($items/price)"/>
<xsl:variable name="label">
<xsl:choose>
<xsl:when test="count($items) = 0">No items</xsl:when>
<xsl:otherwise><xsl:value-of select="count($items)"/> items</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Result tree fragment: use it as a string, not as a node-set -->
<xsl:value-of select="$label"/>
<!-- This is an error in XSLT 1.0: cannot assign twice -->
<!-- <xsl:variable name="total" select="0"/> -->| Construct | Scope | Set by |
|---|---|---|
Top-level xsl:variable | Whole stylesheet | The stylesheet, evaluated at the root |
Template-level xsl:variable | That template | The stylesheet, evaluated once |
xsl:param at top level | Whole stylesheet | External caller or a default value |
xsl:param in a template | That template | with-param or a default |
with-param | The called template only | The calling template |
# Passing parameters in from the environment
xsltproc --stringparam locale en-GB --param limit 25 transform.xsl input.xml
java -cp saxon-he.jar net.sf.saxon.Transform -s:input.xml -xsl:t.xsl \
-o:out.html locale=en-GB limit=25⚠️
A variable whose content is created with
xsl:variable and child instructions instead of select is a result tree fragment in XSLT 1.0. You can string it, but you cannot query it with // or a predicate. Convert it with an extension function or restructure the stylesheet around templates.FAQ
How do I build up a string in a loop?
You cannot append to a variable. Recursion with a named template that carries an accumulator parameter is the 1.0 pattern, and string-join over a sequence is the 2.0 answer.
Are global variables recomputed for every node?
No. A global variable is evaluated once, in the context of the document root, and reused. That is why using position() at global scope is meaningless.
Related
Match patterns versus select expressions Debugging and testing stylesheets
Last refreshed 2026-09-18.