Templates and apply-templates
The processing model behind XSLT: template rules matched against nodes, apply-templates driving traversal, and the built-in rules that fill the gaps.
A stylesheet is a set of rules
XSLT is not a script that loops over a document. A stylesheet is a collection of template rules, and the processor walks the source tree, choosing the best-matching rule for each node. You influence the walk with xsl:apply-templates, which is the only way to move downward.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<h1>Catalog</h1>
<ul>
<xsl:apply-templates select="catalog/book"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<li>
<xsl:value-of select="title"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="price"/>
</li>
</xsl:template>
</xsl:stylesheet>matchdecides which nodes a template handles;selectdecides which nodes get processed.- Literal result elements — plain HTML in the template — are copied to the output; only
xsl:elements are instructions. xsl:textpreserves whitespace exactly, which matters when the stylesheet is indented.xsl:value-ofemits the string value of the first node in the selection — usexsl:for-eachor anotherapply-templatesfor all of them.- Stylesheet whitespace inside a text-only template is stripped, which is why output can lose the spaces you expected.
Built-in rules and modes
If no explicit rule matches, seven built-in rules apply. The two that matter: a template for the root and for elements applies templates to children, and a template for text and attributes copies the value out. That is why a stylesheet with a single rule can still produce readable output.
| Node | Built-in behaviour |
|---|---|
| Root and elements | Apply templates to children |
| Text and attributes | Copy the string value to the output |
| Comments and processing instructions | Nothing |
| Namespace nodes | Nothing |
<!-- suppress a subtree by shadowing the built-in rule -->
<xsl:template match="internal-notes"/>
<!-- process the same node differently in two passes -->
<xsl:template match="book" mode="summary">
<xsl:value-of select="title"/>
</xsl:template>
<xsl:template match="book" mode="detail">
<dl>
<dt>Price</dt><dd><xsl:value-of select="price"/></dd>
<dt>Id</dt><dd><xsl:value-of select="@id"/></dd>
</dl>
</xsl:template>
<!-- call them by name with a mode -->
<xsl:apply-templates select="book" mode="summary"/>
<xsl:apply-templates select="book[@featured]" mode="detail"/>- An empty template body is the idiomatic way to delete nodes from the output.
- Modes let one node have several presentations without duplicating match patterns.
xsl:call-templatewithxsl:with-paramis the escape hatch when you need a subroutine rather than a rule.- Parameters declared with
xsl:paramcan be overridden from the command line, which makes stylesheets reusable.
Running a transformation
# libxslt (XSLT 1.0)
xsltproc --output out.html catalog.xsl catalog.xml
# pass a parameter into the stylesheet
xsltproc --stringparam title "Q3 Catalog" catalog.xsl catalog.xml
# Saxon-HE runs XSLT 3.0 from the command line
java -jar saxon-he.jar -s:catalog.xml -xsl:catalog.xsl -o:out.html
# sanity-check both inputs first
xmllint --noout catalog.xml && xmllint --noout catalog.xslTwo XSLT versions matter in practice: 1.0, which every browser and libxslt implements, and 2.0 or 3.0, which adds sequences, xsl:for-each-group, regular expressions and typed comparisons. Check which engine you have before using modern syntax.
FAQ
Why does nothing appear in the output?
apply-templates. Add <xsl:apply-templates/> inside the root template.Can I still use XSLT in a browser?
Related
Advanced matching and sorting XPath in XSLT and in code
Last refreshed 2026-09-18.