The following associated resources are available: Specification in XML format, XSD 1.1 Schema for XSLT 4.0 Stylesheets (non-normative), Relax-NG Schema for XSLT 4.0 Stylesheets (non-normative), Stylesheet for XML-to-JSON conversion (non-normative)
Copyright © 2026 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
This specification defines the syntax and semantics of XSLT 4.0, a language designed primarily for transforming XML documents into other XML documents.
XSLT 4.0 is a revised version of the XSLT 3.0 Recommendation [XSLT 3.0] published on 8 June 2017. Changes are presented in 1.2 What’s New in XSLT 4.0?.
XSLT 4.0 is designed to be used in conjunction with XPath 4.0, which is defined in [XPath 4.0]. XSLT shares the same data model as XPath 4.0, which is defined in [XDM 3.0], and it uses the library of functions and operators defined in [Functions and Operators 4.0]. XPath 4.0 and the underlying function library introduce a number of enhancements, for example the availability of union and record types.
This document contains hyperlinks to specific sections or definitions within other documents in this family of specifications. These links are indicated visually by a superscript identifying the target specification: for example XP for XPath 4.0, DM for the XDM data model version 4.0, FO for Functions and Operators version 4.0.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
This document has no official standing. It is produced by the editor as a proposal for community review. Insofar as it copies large amounts of text from the W3C XSLT 3.0 Recommendation, W3C copyright and similar provisions apply.
The publications of this community group are dedicated to our co-chair, Michael Sperberg-McQueen (1954–2024).
Template rules define the processing that can be applied to items that match a particular pattern.
[Definition: If no priority attribute is specified on an xsl:template element, a default priority is computed, based on the syntax of the pattern supplied in the match attribute.] The rules are as follows.
If the top-level pattern is a ParenthesizedExprP then the outer parentheses are effectively stripped; these rules are applied recursively to the UnionExprP contained in the ParenthesizedExprP.
If the top-level pattern is a UnionExprP consisting of multiple alternatives separated by | or union, then the template rule is treated equivalently to a set of template rules, one for each alternative. For example, a rule with match="a|b" is treated as if there were one rule with match="a" and another with match="b". These template rules are adjacent to each other in declaration order, and the declaration order within this set of template rules (which affects the result of xsl:next-match if the alternatives have the same default priority) is the order of alternatives in the UnionExprP.
Similarly:
If the top-level pattern takes the form of a UnionNodeTestXP preceded by an axis name or abbreviation, it is treated equivalently to a set of template rules, one for each alternative. For example, a rule with match="@(a|b)" is treated as if there were one rule with match="@a" and another with match="@b".
If the top-level pattern takes the form of an ElementTestXP or AttributeTestXP containing a NameTestUnionXP with multiple alternatives, optionally preceded by a ForwardAxisP, it is treated equivalently to a set of template rules, one for each alternative. For example, a rule with match="element(a|b)" is treated as if there were one rule with match="element(a)" and another with match="element(b)", while a rule with match="attribute(a|b, xs:integer)" is treated as a pair of rules specifying match="attribute(a, xs:integer)" and match="attribute(b, xs:integer)" respectively
If the top-level pattern is a UnionExprP consisting of multiple alternatives separated by | or union, then the default priority is the maximum of the default priorities of the alternatives.
Note:
The splitting of a template rule into multiple rules occurs only if there is no explicit priority attribute.
Splitting the template rule has two effects: firstly, the two match patterns may have different default priority. Secondly, an xsl:next-match instruction within the template body may result in the other template rule being invoked.
In practice, this means that the splitting has no effect if (a) the alternatives have the same default priority, and (b) the alternatives are disjoint — which is the case for some of the examples given above.
This is a backwards incompatible change from XSLT 3.0 and earlier versions, which treated the template rule as equivalent to multiple template rules with different priorities.
The change is made because of the increasing complexity of extending the rule to patterns like @(a|b), or attribute(a|b, xs:integer) which are defined to be semantically identical to equivalent union patterns; and to prevent confusion with type patterns such as type(element(a)|element(b)), where different rules apply.
The change affects any template rule with a union pattern that (a) has no explicit priority attribute, and (b) has multiple branches with different default priority. It is recommended that processors should output a compatibility warning when such template rules are encountered.
The change has two effects. Firstly, if two alternatives in a union pattern have different priority (for example in a pattern such as a | b/c, the priority of one of the branches will be raised to that of the other branch. Secondly, in cases where the same item matches both branches, a call on xsl:next-match will never cause the same template rule to be evaluated repeatedly.
If the top-level pattern is an IntersectExceptExprP containing two or more PathExprP operands separated by intersect or except operators, then the priority of the pattern is that of the first PathExprP.
If the pattern is a PredicatePattern then its priority is 1 (one), unless there are no predicates, in which case the priority is −1 (minus one).
If the pattern is a PathExprP taking the form /, then the priority is −0.5 (minus 0.5).
If the pattern is a PathExprP taking the form of an EQName optionally preceded by a ForwardAxisP or has the form processing-instruction(StringLiteralXP) or processing-instruction(NCNameNames) optionally preceded by a ForwardAxisP, then the priority is 0 (zero).
If the pattern is a PathExprP taking the form of an ElementTestXP or AttributeTestXP, optionally preceded by a ForwardAxisP, then the priority is as shown in the table below. In this table:
The symbols E, A, and T represent an arbitrary element name, attribute name, and type name respectively;
The symbol W represents a WildcardXP other than * (for example prefix:* or *:local);
The symbol * represents itself.
The presence or absence of the symbol ? following a type name does not affect the priority.
| Format | Priority | Notes |
|---|---|---|
element() | −0.5 | (equivalent to *) |
element(*) | −0.5 | (equivalent to *) |
attribute() | −0.5 | (equivalent to @*) |
attribute(*) | −0.5 | (equivalent to @*) |
element(W) | −0.25 | |
attribute(W) | −0.25 | |
element(E) | 0 | (equivalent to E) |
element(*,T) | 0 | (matches by type only) |
attribute(A) | 0 | (equivalent to @A) |
attribute(*,T) | 0 | (matches by type only) |
element(W,T) | 0.125 | |
element(E,T) | 0.25 | (matches by name and type) |
schema-element(E) | 0.25 | (matches by substitution group and type) |
attribute(W,T) | 0.125 | |
attribute(A,T) | 0.25 | (matches by name and type) |
schema-attribute(A) | 0.25 | (matches by name and type) |
If the pattern is a PathExprP taking the form of a DocumentTestXP, then if it includes no ElementTestXP or SchemaElementTestXP the priority is −0.5. If it does include an ElementTestXP or SchemaElementTestXP, then the priority is the same as the priority of that ElementTestXP or SchemaElementTestXP, computed according to the table above.
If the pattern is a PathExprP taking the form of an NCNameNames:*, BracedURILiteralXP*, or *:NCNameNames, optionally preceded by a ForwardAxisP, then the priority is −0.25. [XSLT 3.0 Erratum E37, bug 30375].
If the pattern is a PathExprP taking the form of any other NodeTestXP, optionally preceded by a ForwardAxisP, then the priority is −0.5.
If the pattern is a TypePattern, then the priority is 0 (zero).
In all other cases, the priority is +0.5.
Note:
In many cases this means that highly selective patterns have higher priority than less selective patterns. The most common kind of pattern (a pattern that tests for a node of a particular kind, with a particular expanded QName or a particular type) has priority 0. The next less specific kind of pattern (a pattern that tests for a node of a particular kind and an expanded QName with a particular namespace URI) has priority −0.25. Patterns less specific than this (patterns that just test for nodes of a given kind) have priority −0.5. Patterns that specify both the name and the required type have a priority of +0.25, putting them above patterns that only specify the name or the type. Patterns more specific than this, for example patterns that include predicates or that specify the ancestry of the required node, have priority 0.5.
However, it is not invariably true that a more selective pattern has higher priority than a less selective pattern. For example, the priority of the pattern node()[self::*] is higher than that of the pattern salary. Similarly, the patterns attribute(*, xs:decimal) and attribute(*, xs:short) have the same priority, despite the fact that the latter pattern matches a subset of the nodes matched by the former. Therefore, to achieve clarity in a stylesheet it is good practice to allocate explicit priorities.
<!-- Category: instruction -->
<xsl:apply-imports>
<!-- Content: xsl:with-param* -->
</xsl:apply-imports>
<!-- Category: instruction -->
<xsl:next-match>
<!-- Content: (xsl:with-param | xsl:fallback)* -->
</xsl:next-match>
A template rule that is being used to override another template rule (see 6.5 Conflict Resolution for Template Rules) can use the xsl:apply-imports or xsl:next-match instruction to invoke the overridden template rule. The xsl:apply-imports instruction only considers template rules in imported stylesheet modules; the xsl:next-match instruction considers all template rules that have not already been used. Both instructions will invoke the built-in template rule for the context item (see 6.8 Built-in Template Rules) if no other template rule is found.
[Definition: At any point in the processing of a stylesheet, there may be a current template rule. Whenever a template rule is chosen as a result of evaluating xsl:apply-templates, xsl:apply-imports, or xsl:next-match, the template rule becomes the current template rule for the evaluation of the rule’s sequence constructor.]
The current template rule is cleared (becomes absent) by any instruction that evaluates an operand with changed focus. It is therefore cleared when evaluating instructions contained within:
xsl:copy if and only if there is a select attribute
A global xsl:variable or xsl:param
xsl:template if and only if the called template specifies <xsl:context-item use="absent"/>
Note:
The current template rule is not affected by invoking named attribute sets (see 10.2 Named Attribute Sets), or named templates (see 10.1 Named Templates) unless <xsl:context-item use="absent"/> is specified. While evaluating a global variable or the default value of a stylesheet parameter (see 9.5 Global Variables and Parameters) the current template rule is absent.
These rules ensure that when xsl:apply-imports or xsl:next-match is called, the context item is the same as when the current template rule was invoked.
Both xsl:apply-imports and xsl:next-match search for a template rule that matches the context item, and that is applicable to the current mode (see 6.7 Modes). In choosing a template rule, they use the usual criteria such as the priority and import precedence of the template rules, but they consider as candidates only a subset of the template rules in the stylesheet. This subset differs between the two instructions:
The xsl:apply-imports instruction considers as candidates only those template rules contained in stylesheet levels that are descendants in the import tree of the stylesheet level that contains the current template rule.
Note:
This is not the same as saying that the search considers all template rules whose import precedence is lower than that of the current template rule.
[ERR XTSE3460] It is a static error if an xsl:apply-imports element appears in a template rule declared within an xsl:override element. (To invoke the template rule that is being overridden, xsl:next-match should therefore be used.)
The xsl:next-match instruction considers as candidates all those template rules that come after the current template rule in the ordering of template rules implied by the conflict resolution rules given in 6.5 Conflict Resolution for Template Rules.
This process could be implemented by the following algorithm:
Set a flag active to false.
Follow the rules in 6.5 Conflict Resolution for Template Rules to find the best matching rule, without raising any errors or warnings if there are multiple matches.
If the template rule identified is the current template rule, discard this rule, and repeat the process from step 2 with the flag active set to true.
Otherwise, if active is set to false, discard this rule, and repeat the process from step 2, with the flag active still set to false
Otherwise (if active is set to true) use the selected rule.
Note:
An alternative implementation would be to maintain, not just the current template rule, but a list of rules that have been used to process the context item. The implementation of xsl:next-match can then eliminate these rules from the search.
In the absence of type patterns, it is possible to define a total ordering of template rules for each mode, and to exclude those rules that appear before the current template rule in this ordering. The introduction of type patterns makes this approach more challenging, since types are partially ordered.
Note:
As explained in 6.5 Conflict Resolution for Template Rules, a template rule with no priority attribute, whose match pattern contains multiple alternatives separated by |, is treated equivalently to a set of template rules, one for each alternative. This means that where the same item matches more than one alternative, it is possible for an xsl:next-match instruction to cause the current template rule to be invoked recursively. This situation does not occur when the template rule has an explicit priority.
Note:
Because a template rule declared as a child of xsl:override has higher precedence than any template rule declared in the used package (see 3.5.4 Overriding Template Rules from a Used Package), the effect of xsl:next-match within such a template rule is to consider as candidates first any other template rules for the same mode within the same xsl:use-package element (taking into account explicit and implicit priority, and document order, in the usual way), and then all template rules in the used package.
If no matching template rule is found, both xsl:apply-imports and xsl:next-match cause the built-in template rule for the mode to be invoked.
If multiple matching template rules with the same explicit or implicit priority are found, both xsl:apply-imports and xsl:next-match respect the on-multiple-match and warning-on-multiple-match attributes of the mode declaration.
Note:
If is entirely possible for xsl:apply-templates to identify a template rule unambiguously, and for xsl:apply-imports or xsl:next-match then to fail because there is no unambiguous second-choice template rule.
If a matching template rule R is found, then the result of the xsl:next-match or xsl:apply-imports instruction is the result of invoking R, with the values of parameters being set using the child xsl:with-param elements as described in 9.10 Setting Parameter Values. The template rule R is evaluated with the same focus as the xsl:next-match or xsl:apply-imports instruction. The current template rule changes to be R. The current mode does not change.
Note:
In the case where the current template rule T is declared within an xsl:override element in a using package P, while the selected rule R is declared within a different package Q, and where the current mode is MP (mode M in package P), the effect is that the current mode for evaluation of R remains MP rather than reverting to its corresponding mode MQ (mode M in package Q). If R contains an xsl:apply-templates instruction that uses mode="#current", then the set of template rules considered by this instruction will therefore include any overriding template rules declared in P as well as the original rules declared in Q.
If no matching template rule is found that satisfies these criteria, the built-in template rule for the context item is used (see 6.8 Built-in Template Rules).
An xsl:apply-imports or xsl:next-match instruction may use xsl:with-param child elements to pass parameters to the chosen template rule (see 9.10 Setting Parameter Values). It also passes on any tunnel parameters as described in 10.1.6 Tunnel Parameters.
[ERR XTDE0560] It is a dynamic error if xsl:apply-imports or xsl:next-match is evaluated when the current template rule is absent.
For example, suppose the stylesheet doc.xsl contains a template rule for example elements:
<xsl:template match="example"> <pre><xsl:apply-templates/></pre> </xsl:template>
Another stylesheet could import doc.xsl and modify the treatment of example elements as follows:
<xsl:import href="doc.xsl"/> <xsl:template match="example"> <div style="border: solid red"> <xsl:apply-imports/> </div> </xsl:template>
The combined effect would be to transform an example into an element of the form:
<div style="border: solid red"><pre>...</pre></div>
An xsl:fallback instruction appearing as a child of an xsl:next-match instruction is ignored by an XSLT 2.0 or 3.0 processor, but can be used to define fallback behavior when the stylesheet is processed by an XSLT 1.0 processor with forwards compatible behavior.
Use the arrows to browse significant changes since the 3.0 version of this specification.
Sections with significant changes are marked Δ in the table of contents.
Named item types can be declared using the new xsl:item-type element. This is designed to avoid repeating lengthy type definitions (for example function types) every time they are used. [This feature was present in the editor's draft presented to the WG when it started work.]
The xsl:for-each and xsl:apply-templates instructions acquire an attribute separator that can be used to insert content between adjacent items. [This change was in the editor's draft adopted as a baseline when the WG commenced work.]
PR 751 1386
The result type of a mode can be declared using an as attribute. The result type of all template rules in this mode must be consistent with this, as must the values returned by any built-in template rules for the mode.
The xsl:for-each and xsl:apply-templates instructions acquire an attribute separator that can be used to insert content between adjacent items. [This change was in the editor's draft adopted as a baseline when the WG commenced work.]
Numeric values of type xs:decimal are compared as decimals, without first converting to xs:double.
Functions that accept a lexical QName as an argument, such as key, function-available, element-available, type-available, system-property, accumulator-before, and accumulator-after, now have the option of supplying an xs:QName value instead. [This change was in the editor's draft accepted by the WG as its baseline when it started work.]
Functions that accept a lexical QName as an argument, such as key, function-available, element-available, type-available, system-property, accumulator-before, and accumulator-after, now have the option of supplying an xs:QName value instead. [This change was in the editor's draft accepted by the WG as its baseline when it started work.]
It is possible to invoke a named template using an extension instruction, specifically, an element whose name matches the name of the named template.
See 10.1.3 Invoking Named Templates using Extension Instructions
A new attribute xsl:map/@on-duplicates is available, allowing control over how duplicate keys are handled by the xsl:map instruction.
A new attribute xsl:for-each-group/@split-when is available to give applications more complete control over how a sequence is partitioned
See 14 Grouping
Duplicate xsl:include declarations within a stylesheet level are now ignored, preventing spurious errors caused by the presence of duplicate named components.
Named record types are introduced.
The contents of a character map declared using xsl:character-map are now available dynamically via a new character-map function.
New variables err:stack-trace, err:additional, and err:map are available within an xsl:catch clause.
See 8.4 Try/Catch
The input to the serializer can be defined using the select attribute of xsl:result-document as an alternative to using a sequence constructor.
It is no longer an intrinsic error for a global variable to refer to itself; this is now permitted, for example in cases where the value of the global variable is a recursive inline function. Cases where self-reference would not make sense are covered by the existing rules on circularities: see 9.11 Circular Definitions.
The default value for the indent parameter is now defined to be no for all output methods other than html and xhtml.
The xsl:map instruction allows a select attribute as an alternative to the contained sequence constructor.
The xsl:map-entry instruction, in common with other instructions, now raises error XTSE3185 (rather than XTSE3280) if both a select attribute and a sequence constructor are present.
Composite sort keys are allowed in xsl:sort.
The default priority for a template rule using a union pattern has changed. This change may cause incompatible behavior.
PR 159
Parameters on functions declared using xsl:function can now be defined as optional, with a default value supplied.
PR 237
The xsl:if instruction now allows then and else attributes.
See 8.1 Conditional Processing with xsl:if
In xsl:choose, the xsl:when and xsl:otherwise elements can take a select attribute in place of a sequence constructor.
See 8.2 Conditional Processing with xsl:choose
A new xsl:switch instruction is introduced.
PR 326
The higher-order-function feature no longer exists; higher-order functions are now a core part of XSLT, no longer an optional extra.
See 28 Conformance
PR 353
A new attribute, main-module, is added to the xsl:stylesheet element. The attribute is provided for the benefit of development tools such as syntax-directed editors to provide information about all the components (variables, functions, etc) visible within a stylesheet module.
A new element xsl:note is available for documentation and similar purposes: it can appear anywhere in the stylesheet and is ignored by the XSLT processor.
PR 401
Patterns (especially those used in template rules) can now be defined by reference to item types, so any item type can be used as a match pattern. For example match="record(longitude, latitude, *)" matches any map that includes the key values "longitude" and "latitude".
PR 406
The new instruction xsl:array is introduced to allow construction of arrays.
PR 470
The xsl:stylesheet, xsl:transform, or xsl:package element may have a fixed-namespaces attribute making it easier to have the same namespace declarations in force throughout a stylesheet.
PR 489
The xsl:matching-substring and xsl:non-matching-substring elements within xsl:analyze-string may now take a select attribute in place of a contained sequence constructor.
PR 534
A new serialization parameter escape-solidus is provided to control whether the character / is escaped as \/ by the JSON serialization method.
PR 542
A mode (called an enclosing mode) can be defined in which all the relevant template rules are children of the xsl:mode element. This is intended to allow a stylesheet design in which it is easier to determine which rules might apply to a given xsl:apply-templates call.
PR 599
Simplified stylesheets no longer require an xsl:version attribute (which means they might not need a declaration of the XSLT namespace). Unless otherwise specified, a 4.0 simplified stylesheet defaults expand-text to true.
PR 635
The rules concerning the compatibility of schemas imported by different packages have been clarified. It is now explicitly stated that instructions that trigger validation must use the imported schema of the package in which validation is invoked. This differs from the current practice of some XSLT 3.0 processors, which may use (for example) a schema formed from the union of the imported schemas in all packages.
See 3.15 Importing Schema Components
See 26.4 Validation
PR 717
Capturing accumulators have been added; when streaming with a capturing accumulator, the accumulator-after has full access to a snapshot of the matched element node.
PR 718
To allow recursive-descent transformation on a tree of maps and arrays, a new set of built-in templates rules shallow-copy-all is introduced.
PR 751
The xsl:mode declaration acquires an attribute as="sequence-type" which declares the return type of all template rules in that mode.
PR 1181
The [xsl:]xpath-default-namespace attribute can be set to the value ##any, which causes unprefixed element names to match in any namespace or none.
See 5.1.2 Unprefixed Lexical QNames in Expressions and Patterns
PR 1250
The strings used in the formatted number to represent a decimal separator, grouping separator, exponent separator, percent sign, per mille sign, or minus sign, are no longer constrained to be single characters.
PR 1254
The rules concerning the interpretation of xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes have been tightened up.
See 26.4 Validation
PR 1306
An as attribute is available on the xsl:sequence instruction.
PR 1361
The term atomic value has been replaced by atomic item.
See 2.1 Terminology
PR 1378
A function call at the outermost level can now be named using any valid EQName (for example fn:doc) provided it binds to one of the permitted functions fn:doc, fn:id, fn:element-with-id, fn:key, or fn:root. If two functions are called, for example doc('a.xml')/id('abc'), it is no longer necessary to put the second call in parentheses.
PR 1442
Default priorities are added for new forms of ElementTest and AttributeTest, for example element(p:*) and element(a|b).
PR 1622
The rules for equality comparison have changed to bring keys into line with maps.
See 20.2.2 fn:key
New in 4.0.
PR 1689
Composite merge keys are now allowed.
See 15 Merging
PR 1703
Ordered maps are introduced.
This section lists all known incompatibilities with XSLT 3.0, that is, situations where a stylesheet that is error-free according to the XSLT 3.0 specification and where all elements have an effective version of 3.0 or less, will produce different results depending on whether it is run under an XSLT 3.0 processor or an XSLT 4.0 processor.
The rules for comparing values in xsl:for-each-group now reference the rules for distinct-values, which have themselves changed to be compatible with fn:atomic-equal. This change eliminates the intransitivity in the previous specification, which meant that in edge cases involving rounding of numeric values of different types, two items in different groups could compare equal. Any change in behavior is confined to this edge case.
The rules for comparing values in keys have changed, to align with the rules for maps. The changes affect edge cases involving rounding of numeric values of different types, and the comparison of date/time values with and without timezones.
In previous versions, a template rule whose match pattern was a union pattern and that had no explicity priority was treated as equivalent to a set of template rules, one for each alternative in the union, each potentially with different default priority. This rule has not been carried forward into XSLT 4.0: instead, the priority of the rule is taken as being the highest priority of any of the alternatives.
This specification also corrects a number of errors and omissions in XSLT 3.0, in a way that might create incompatibilities for some processors, depending on how they interpreted the XSLT 3.0 specification:
XSLT 3.0 (and earlier releases) did not fully define the evaluation context for the default values of template parameters. For example, if the default value of a parameter of a template rule invoked xsl:next-match, it was not specified whether the current template rule should be the calling template or the called template. This omission has been corrected.
Where different packages import different schemas, the specification is now more prescriptive about which schema is used for validation. The rules may differ from the conventions adopted by implementations of XSLT 3.0.
XSLT 3.0 failed to specify a default value for the serialization parameter indent where the serialization method is json or adaptive. XSLT 4.0 specifies a default value of no.
See also [XPath 4.0], [Functions and Operators 4.0], and [XSLT and XQuery Serialization 4.0] for incompatibilities in other related specifications that may affect XSLT stylesheets.