XSLT versions, engines and where transformations run
XSLT 1.0 is universal and XSLT 3.0 is far more capable, but the version you can use is decided by the engine you deploy.
The three versions in practice
| Version | Year | Key additions | Typical engine |
|---|---|---|---|
| 1.0 | 1999 | Templates, XPath 1.0, basic output control | libxslt, Xalan, browser engines, .NET |
| 2.0 | 2007 | Sequences, types, grouping, regex, user functions | Saxon 9, Xalan-J 2.7 (partial) |
| 3.0 | 2017 | Maps, arrays, streaming, packages, higher-order functions | Saxon 9.7+, SaxonJS |
- Everything published as a browser transform is 1.0, because no browser shipped a 2.0 engine.
libxsltis the engine behindxsltproc, PHP'sxslextension and many Python bindings. It is 1.0 plus a handful of extension functions.- .NET's
XslCompiledTransformis 1.0; .NET does not ship a 2.0 or 3.0 processor. - Java's JAXP defaults to Xalan 1.0 but can be pointed at Saxon by setting a system property, which is the usual route to 2.0 in a JVM.
- If you need grouping, regex or date arithmetic, choose the engine before writing a single template.
# xsltproc (libxslt) -- XSLT 1.0, installed on most Linux systems
xsltproc --output out.html transform.xsl input.xml
# Saxon-HE on the command line -- XSLT 3.0, free edition
java -cp saxon-he-12.5.jar net.sf.saxon.Transform -s:input.xml -xsl:transform.xsl -o:out.html
# Passing a parameter from the shell
xsltproc --stringparam locale en-GB transform.xsl input.xmlWhere the transform should run
| Placement | Use it when | Watch out for |
|---|---|---|
| Build time | Static site generation, report templates | Failure stops the build, which is what you want |
| Server side | Input arrives per request, output must be fresh | Per-request engine warm-up cost |
| Client side | Legacy intranet pages, XML data islands | Only 1.0, slow, and blocked in some browsers |
| Inside the database | Very large documents that must not leave the server | Vendor-specific dialects and painful debugging |
from lxml import etree
# Server-side transform, 1.0 via libxslt, compiled once and reused
transform = etree.XSLT(etree.parse("transform.xsl"))
result = transform(etree.parse("input.xml"), locale="en-GB")
print(str(result))⚠️
A browser transform means the XML and the stylesheet are both fetched by the client, so anything in them is public. Never put credentials, internal URLs or unpublished data in a document you intend to transform in the browser.
FAQ
Can I write one stylesheet that runs on both 1.0 and 3.0 engines?
Yes, by marking the stylesheet version="1.0" and avoiding 2.0 features. Saxon always runs a 1.0 stylesheet in backwards-compatible mode, so the semantics stay 1.0.
Is XSLT still worth learning?
For XML-to-XML and XML-to-office-document work it remains the shortest path, and XSLT 3.0 is genuinely expressive. For HTML from a database, a template language in your application is usually easier to maintain.
Related
XSLT 2.0 and 3.0 beyond version 1.0 Calling XSLT from Java, .NET and Python
Last refreshed 2026-09-18.