XML doesn't come up in every stack, but when it does — RSS feeds, SOAP APIs, legacy enterprise systems, config files for older Java or .NET apps, SVG — you eventually need to reshape it into something else: HTML for display, a different XML schema, plain text, even another data format entirely. That's what XSL (specifically XSLT, XSL Transformations) is for, and it trips people up because it looks like a template language but works more like a functional, pattern-matching one.
Here's a practical walkthrough of what's actually going on.
What XSL actually is
XSL is a family of three specs:
XSLT — the transformation language itself (XML in, XML/HTML/text out)
XPath — the query language XSLT uses internally to select nodes
XSL-FO — a formatting spec for print/PDF output (much less commonly used today)
When people say "XSL," they almost always mean XSLT. It's a declarative, rule-based language: instead of writing a loop that walks the XML tree, you write templates that match patterns in the tree, and the processor applies whichever template matches each node.
A minimal example
Given this XML:
<catalog>
<book id="1">
<title>Refactoring</title>
<author>Martin Fowler</author>
</book>
<book id="2">
<title>Clean Code</title>
<author>Robert Martin</author>
</book>
</catalog>
A basic XSLT stylesheet to turn this into an HTML table:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/catalog">
<table>
<tr><th>Title</th><th>Author</th></tr>
<xsl:for-each select="book">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="author"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
A few things worth noticing:
match="/catalog" is an XPath expression selecting the root node — this is the "rule-based" part, XSLT is matching a pattern, not iterating an index.
xsl:for-each loops over matched nodes, but idiomatic XSLT (especially version 2.0+) often prefers separate rules over for-each, since it composes better as documents get more complex.
xsl:value-of extracts text content from a node — this is your "print this value" primitive.
Where this actually gets used day to day
RSS/Atom feeds — browsers historically used XSLT to render raw XML feeds as readable HTML pages when someone opened the feed URL directly.
SOAP/enterprise APIs — transforming one XML schema into another when integrating systems that don't speak the same schema natively.
SVG post-processing — SVG is XML, and XSLT can restructure or strip SVG markup programmatically.
Legacy CMS and publishing pipelines — a lot of older docs/publishing systems store content as XML and use XSLT to render multiple output formats (HTML, print, EPUB) from one source.
The parts that trip people up
XPath axes (ancestor::, following-sibling::, descendant::) are the thing most JS/Python developers have never had to think about, because most modern data formats don't need tree-relative queries this explicit. Budget time to actually learn XPath separately from XSLT — it pays off.
XSLT 1.0 vs 2.0/3.0 matters a lot for what's available. Browsers largely only support XSLT 1.0 natively; if you need 2.0/3.0 features (better string functions, grouping), you need a processor like Saxon rather than relying on browser-native transformation.
Namespace handling is a common source of "why isn't my template matching anything" bugs — if the source XML has a default namespace, your XSLT templates need matching namespace declarations, or nothing will match.
When you just need a quick transform
Learning XSLT properly is worth it if you're doing this regularly, but for a one-off — checking what an XML document looks like after a transform, or generating a quick HTML preview from an XML file without setting up a full XSLT processor locally — I built a small XML to XSL converter that runs the transformation in the browser. Useful for sanity-checking a stylesheet or getting unblocked without installing Saxon or configuring a build step for a five-minute task.
Wrapping up
XSLT feels unfamiliar mostly because it's declarative and pattern-matching in a way most mainstream languages aren't anymore — but the mental model (match a node shape, describe what to output for it) is genuinely elegant once it clicks, and it's still doing quiet, unglamorous work in a lot of production systems.
Anyone else still maintaining XSLT in a legacy system? Curious what's kept it alive in your stack versus getting replaced.
Top comments (0)