Match patterns versus select expressions

A stylesheet contains two different XPath dialects, and confusing them produces templates that never fire or select far more than intended.

Two dialects, one syntax

match on a templateselect on apply-templates / value-of
PurposeDescribe which nodes this template handlesChoose nodes to process or output
EvaluationPattern matching against one node at a timeA real XPath expression from the context node
Relative toThe whole documentThe current context node
Return valueMust be a patternAny XPath type
Functions allowedOnly id() and key()All of them
Order mattersNo, priority decidesYes, step order decides
<xsl:template match="item">            <!-- fires for any item element -->
<xsl:template match="list/item">       <!-- only items whose parent is list -->
<xsl:template match="item[@status='new']">
<xsl:template match="//item">          <!-- legal in 1.0 only as id()/key() sugar;
                                            prefer match="item", which also matches at any depth -->

<xsl:apply-templates select="item[price > 20]"/>   <!-- select is a real expression -->
<xsl:apply-templates select="item" mode="summary"/>

A pattern is matched against a node with the document as the starting point, so match="list/item" means "an item whose parent is a list", regardless of how the node was reached. A select is evaluated with the current node as the context, so select="list/item" inside a template that matches the root means something completely different.

The context item, position and last()

<xsl:template match="li">
  <!-- position() is the position among the nodes being processed by apply-templates -->
  <xsl:if test="position() = 1">
    <p>First item: <xsl:value-of select="."/></p>
  </xsl:if>
  <xsl:if test="position() = last()">
    <p>Last item: <xsl:value-of select="."/></p>
  </xsl:if>
</xsl:template>
  • position() depends on the node-list passed to apply-templates, not on the element's index in the document.
  • In XSLT 1.0 current() returns the node being matched at the outermost level, which is how you reference it inside a predicate.
  • In XSLT 2.0 and later, current() refers to the current template rule's item, and . is the context item — they diverge inside for-each.
  • last() inside a predicate like item[last()] is the size of the node-list being filtered, not the size of the whole document.
<!-- current() keeps the outer node available while a predicate moves the focus -->
<xsl:template match="product">
  <xsl:value-of select="//price[@sku = current()/@sku]"/>
</xsl:template>
💡
Priorities resolve most conflicts, not document order: a pattern with a predicate or a longer path beats a shorter one. When two patterns have equal priority the later declaration wins, which is why a catch-all match="*" belongs at the bottom of the file.

FAQ

Why is my template never called?
Usually because nothing applies templates to that node. A template only runs when apply-templates reaches a matching node, or when the node is the initial node and matches the root pattern.
Can I put a function call in a match attribute?
No. Patterns allow only id() and key(), and even those are restricted in form. Move the logic into a select expression or a variable instead.

Templates and apply-templates Advanced matching and sorting

Last refreshed 2026-09-18.