Source Code: github.com/spagu/XSLT-Processor
JavaScript implementation of XSLTProcessor for browser environments and Node.js CLI. This package provides a complete implementation of the W3C XSLTProcessor API that can be used as a drop-in replacement for the native browser implementation.
Chrome and other browsers are removing native XSLT (Chrome's announcement):
- Chrome 143 (December 2025):
XSLTProcessorand<?xml-stylesheet type="text/xsl"?>are deprecated, with warnings in the console - Chrome 158 (17 November 2026): XSLT stops working in stable Chrome, except for sites in the origin trial and browsers under the enterprise policy
- Chrome 176 (17 August 2027): the origin trial and the enterprise policy end; XSLT is off everywhere
- Firefox and WebKit support the removal but have not announced dates
This library ensures your XSLT-based applications continue to work regardless of browser support.
Used by Schema Resume, Job Tailor and others: see References.
- 1:1 Native API Compatibility: Drop-in replacement for native
XSLTProcessor - One-line migration:
import "@tradik/xslt-processor/polyfill"or a<script>tag keepsXSLTProcessorpages working, and the same tag inside an XML document keeps<?xml-stylesheet?>rendering;npx xslt-migrate-check .finds the pages that need it,--fixwrites the patch andxslt-migrate-testcompares the output with the browser's (wizard, how-to) - XSLT 1.0: Every element and function of the W3C XSLT 1.0 Recommendation, with the few gaps listed under Known Deviations
- XPath 1.0 Engine: Built-in XPath evaluator with all core functions
xsl:outputSerialization:transformToString()honors method, indent, doctype, CDATA sections anddisable-output-escaping- Zero Dependencies: The library has no runtime dependencies; only the
xsltcommand line tool needsjsdom(an optional peer dependency) - Multiple Formats: ESM, CommonJS, and browser IIFE bundles
- TypeScript Support: Includes TypeScript declarations
The documentation is also published as a website with an interactive playground: https://xslt-processor.tradik.com/ (playground: XSLT 1.0 transformations, and with the in-development @tradik/xslt3 XSLT 3.0 stylesheets at ?mode=xslt3 and XPath 3.1 expressions at ?mode=xpath; blog with an RSS feed).
| Guide | Contents |
|---|---|
| API Reference | XSLTProcessor methods, parameters, xsl:output serialization, module exports, XsltEngine options, TypeScript |
| Loaders | xsl:import, xsl:include and the document() function |
| Complete Example | A product list transformed into an HTML table (browser and CLI) |
| Command Line Tool | xslt options, base directory, includes, input encodings |
| Conformance | W3C compliance tables, supported elements and functions, known deviations, test coverage |
| Security Limits | XPath and XSLT limits, prototype pollution protection, input validation |
| Browser Compatibility | Minimum browser versions, native XSLT removal timeline, feature detection |
| XSLT 2.0 and 3.0 | @tradik/xslt3: one engine for XSLT 3.0 and 2.0 in a separate package; options, conformance and design |
| Migration checker | npx xslt-migrate-check .: finds XSLTProcessor usages, <?xml-stylesheet?> documents and stylesheets in a project, rates the Chrome risk and prints the one-line migration; --json and --fail-on for CI; online at xslt-processor.tradik.com/check |
| Migration fixes | npx xslt-migrate-check . --fix: the polyfill import, the script tags and the dependencies written as migration.patch for review (--write applies it); xslt-migrate-check/analyze runs the same analysis in a browser |
| Migration test | xslt-migrate-test: runs every XML + XSL pair on the browser's engine (Chromium) or libxslt and on this library, compares the output and reports the compatibility |
| Benchmarks | Every version side by side (1.1.3, 1.2.0, 1.3.0, @tradik/xslt3) in one chart and table; then the detailed runs: 1.1.3 vs 1.2.0 speed-up, time and peak memory per scenario, with charts, tables and npm run bench to reproduce; XPath 1.0 of this package vs XPath 3.1 of @tradik/xslt3 (npm run bench -- --suite xpath); the XSLT 1.0 engine vs @tradik/xslt3 on the same stylesheets, idiomatic 2.0/3.0 rewrites and 3.0-only scenarios (npm run bench -- --suite xslt) |
| Development | Setup, tests, Docker, Makefile, publishing to npm |
| Style Guide | Project colors with WCAG 2.2 contrast ratios |
All guides are listed in the documentation index.
npm install @tradik/xslt-processor
# or
yarn add @tradik/xslt-processor
pnpm add @tradik/xslt-processor
bun add @tradik/xslt-processorEvery change is installed and smoke-tested in CI with npm, Yarn 1, Yarn 4
(Plug'n'Play), pnpm and bun (npm run test:package-managers). The XSLT 2.0/3.0
engine installs the same way (yarn add @tradik/xslt3 and so on), and the
migration checker runs without installing: npx xslt-migrate-check .,
pnpm dlx xslt-migrate-check . or bunx xslt-migrate-check ..
The library itself has no dependencies. The xslt command line tool also
needs jsdom (an optional peer dependency, >=25), see CLI Usage.
Use a CDN for the easiest browser integration - no build step required:
<!-- jsDelivr (recommended) -->
<script src="https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script>
<!-- or unpkg -->
<script src="https://unpkg.com/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script>
<script>
// XSLTProcessor is the native one, or this polyfill when native XSLT is unavailable
const processor = new XSLTProcessor();
// Load and transform XML
const parser = new DOMParser();
const xslt = parser.parseFromString(xsltString, 'application/xml');
const xml = parser.parseFromString(xmlString, 'application/xml');
processor.importStylesheet(xslt);
const result = processor.transformToFragment(xml, document);
document.getElementById('output').appendChild(result);
</script>The bundle defines the global XsltProcessorLib (all module exports)
and calls installGlobal(): window.XSLTProcessor is replaced only when the
browser has no working native implementation. To always use this
implementation, call XsltProcessorLib.installGlobal(true) or use
new XsltProcessorLib.XSLTProcessor().
CDN URLs:
| CDN | URL |
|---|---|
| jsDelivr | https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js |
| unpkg | https://unpkg.com/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js |
Tip: Use
@1for the latest 1.x version, or an exact version such as@1.1.3for pinning.
An XML file that Chrome rendered through <?xml-stylesheet type="text/xsl"?>
shows up as raw XML once native XSLT is gone. One line right after the
processing instruction keeps it rendering: browsers without XSLT run an XHTML
<script> found in an XML document, the bundle finds the instruction,
fetches the stylesheet (and its imports), transforms the document and
replaces it with the result, scripts of the result included.
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="catalog.xsl"?>
<catalog><script xmlns="http://www.w3.org/1999/xhtml" src="https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script>
<item>…</item>
</catalog>Browsers that still have XSLT apply the instruction themselves and never see
the script. The stylesheet does not see it either: it is removed from the
source before the transformation. To find the pages that need this line, run
npx xslt-migrate-check . in your project (migration checker).
If you prefer local installation:
<script src="node_modules/@tradik/xslt-processor/dist/xslt-processor.browser.min.js"></script>
<script>
// XSLTProcessor is now available globally
const processor = new XSLTProcessor();
// ...
</script>import "@tradik/xslt-processor/polyfill"; // first, before code that uses XSLTProcessor
const processor = new XSLTProcessor(); // the native one, or this library where there is noneThe entry exports nothing: it installs this XSLTProcessor as the global one
when the browser has no working native XSLT (installGlobal()), and applies an
<?xml-stylesheet?> the browser left unapplied (autoApplyXmlStylesheet()).
In browsers that still have XSLT it changes nothing. CommonJS:
require("@tradik/xslt-processor/polyfill").
import { XSLTProcessor, installGlobal } from '@tradik/xslt-processor';
// Optional: Force install as global XSLTProcessor
installGlobal();
// Or use directly
const processor = new XSLTProcessor();
const parser = new DOMParser();
// Load and parse XSLT stylesheet
const xsltText = await fetch('template.xsl').then(r => r.text());
const xsltDoc = parser.parseFromString(xsltText, 'application/xml');
processor.importStylesheet(xsltDoc);
// Load and parse XML source
const xmlText = await fetch('data.xml').then(r => r.text());
const xmlDoc = parser.parseFromString(xmlText, 'application/xml');
// Transform
const fragment = processor.transformToFragment(xmlDoc, document);
document.getElementById('output').appendChild(fragment);const { XSLTProcessor } = require('@tradik/xslt-processor');
const processor = new XSLTProcessor();
// ...Node.js has no DOM, so bring one such as jsdom. The
filesystem loader example
is a complete Node.js script, and the API Reference covers every
method.
By default every stylesheet runs with the XSLT 1.0 engine, and a
version="2.0" stylesheet runs in forwards-compatible mode, as in Chrome.
With xsltVersion: "auto", a stylesheet that declares version 2.0 or 3.0 is
run by @tradik/xslt3, an optional peer dependency loaded
with import() only when such a stylesheet is imported, so the XSLT 1.0
bundles do not grow by the size of the new engine:
// npm install @tradik/xslt3
const processor = new XSLTProcessor({ xsltVersion: "auto" });
const html = await processor.transformAsync(xmlText, { stylesheet: xsl20Text });
// The synchronous W3C API needs the engine loaded first
await XSLTProcessor.preload();
processor.importStylesheet(xsl20Doc);
const fragment = processor.transformToFragment(xmlDoc, document);The CLI flag is --xslt-version auto. Details: Opt-in XSLT 2.0/3.0.
The package includes a command-line tool for transforming XML documents.
Without Node.js, use the standalone executable attached to every GitHub release (Linux x64/arm64, macOS x64/arm64, Windows x64), installed with a checksum check:
curl -fsSL https://raw.githubusercontent.com/spagu/XSLT-Processor/main/scripts/install.sh | bashSee Standalone executables for manual
installation. With Node.js, the command line tool needs a DOM implementation, so install jsdom next to
the package. It is an optional peer dependency: library users do not need it.
Without it, xslt exits with an explanation instead of a stack trace.
# Global installation
npm install -g @tradik/xslt-processor jsdom
# Transform XML with XSLT
xslt data.xml template.xsl
# Save output to file
xslt data.xml template.xsl -o result.html
# With parameters
xslt data.xml template.xsl -p title="My Page" -p count=10
# Format output with indentation
xslt data.xml template.xsl -f -o output.html
# Override the output method and drop the XML declaration
xslt data.xml template.xsl --method text
xslt data.xml template.xsl --no-declaration
# Run an XSLT 2.0/3.0 stylesheet with @tradik/xslt3 (npm install -g @tradik/xslt3)
xslt data.xml grouping.xsl --xslt-version autoSee Command Line Tool for all options, the base directory,
xsl:include/document() resolution and input encodings.
The main differences from XSLT 1.0 / XPath 1.0 and from libxslt:
xsl:numberignoreslangandletter-value;unparsed-entity-uri()always returns'';- numbers convert to strings in the XPath 1.0 form (
10000000000), where libxslt writes1e+10.
Details and workarounds: Known Deviations.
- CONTRIBUTORS.md - how to contribute
- CODE_OF_CONDUCT.md
- SECURITY.md - reporting vulnerabilities and the threat model
- CHANGELOG.md - release history
BSD-3-Clause License - see LICENSE.md for details.
- MDN XSLTProcessor
- libxslt - Reference implementation in C