Chapter 67: XSLT On the Client
1. What does “XSLT on the client” mean?
Client-side XSLT = the browser itself reads an XML file + an XSLT stylesheet and performs the transformation in the browser, without any server-side code (PHP, Java, Node.js, Python…).
Two main ways this happens:
Way A – Pure XML file with <?xml-stylesheet?> processing instruction (the classic, oldest way – still works in 2025)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="products-to-html.xsl"?> <catalog> <product> <name>Wireless Mouse</name> <price currency="INR">1499.00</price> </product> ... </catalog> |
When you open this file directly in the browser (file:///…/catalog.xml or http://localhost/catalog.xml), the browser:
- Sees the <?xml-stylesheet?> line
- Downloads the referenced .xsl file
- Applies the transformation
- Displays the resulting HTML
Way B – JavaScript loads XML + XSL and performs transformation
This is the modern, controlled way (still used in 2025 when you need to:
- load XML dynamically via AJAX/fetch
- choose different stylesheets
- handle errors
- combine with other JavaScript logic
2. Which browsers still support native client-side XSLT in 2025?
| Browser | Native XSLT 1.0 support | Native XSLT 2.0/3.0 support | <?xml-stylesheet?> works | JavaScript XSLTProcessor works | Comment (2025) |
|---|---|---|---|---|---|
| Chrome / Chromium | Yes (XSLT 1.0) | No | Yes | Yes | Works reliably |
| Edge (Chromium) | Yes | No | Yes | Yes | Same as Chrome |
| Firefox | Yes | No | Yes | Yes | Very good support |
| Safari | Yes (partial) | No | Yes | Yes | Usually works, but sometimes buggy |
| Opera | Yes | No | Yes | Yes | Chromium-based |
Important reality check 2025–2026
- XSLT 1.0 is still very well supported in all major browsers
- XSLT 2.0 / 3.0 → no native browser support (you need Saxon-JS or other JavaScript libraries)
- Most real-world client-side XSLT today uses XSLT 1.0
3. Example 1 – Classic method: <?xml-stylesheet?> (no JavaScript)
File: products.xml
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="products-to-html.xsl"?> <catalog> <product> <name>Wireless Mouse</name> <price currency="INR">1499.00</price> <stock>45</stock> </product> <product> <name>USB-C Hub</name> <price currency="INR">2499.00</price> <stock>12</stock> </product> </catalog> |
File: products-to-html.xsl
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?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" encoding="UTF-8"/> <xsl:template match="/"> <html> <head> <title>Product Catalog</title> <style> table { border-collapse: collapse; width: 80%; margin: 20px auto; } th, td { border: 1px solid #ccc; padding: 10px; } th { background: #f0f0f0; } .low { color: red; font-weight: bold; } </style> </head> <body> <h1>Products</h1> <table> <tr> <th>Name</th> <th>Price</th> <th>Stock</th> </tr> <xsl:for-each select="catalog/product"> <tr> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/> <xsl:value-of select="price/@currency"/></td> <td> <xsl:choose> <xsl:when test="stock &lt; 20"> <span class="low"><xsl:value-of select="stock"/></span> </xsl:when> <xsl:otherwise> <xsl:value-of select="stock"/> </xsl:otherwise> </xsl:choose> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet> |
How to test:
- Put both files in the same folder
- Open products.xml directly in Chrome, Edge, Firefox or Safari
- You should see a nice styled table
Advantages of this method:
- No JavaScript needed
- Very simple deployment
- Works offline (file:// protocol)
Disadvantages:
- No error handling
- No dynamic loading
- No way to choose stylesheet dynamically
- No way to pass parameters
4. Example 2 – Modern way: JavaScript + fetch + XSLTProcessor
This is the recommended way in 2025 when you want control.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XSLT on Client – JavaScript way</title> <style> body { font-family: Arial; margin: 40px; } button { padding: 12px 24px; background: #10b981; color: white; border: none; border-radius: 6px; cursor: pointer; } #result { margin-top: 30px; border: 1px solid #ccc; padding: 20px; min-height: 200px; } </style> </head> <body> <h2>XSLT Transformation in Browser (JavaScript)</h2> <button onclick="transform()">Load & Transform products.xml</button> <div id="result">Click the button to see transformed content...</div> <script> async function transform() { const resultDiv = document.getElementById("result"); resultDiv.innerHTML = "Loading XML and stylesheet..."; try { // 1. Load XML const xmlResponse = await fetch("catalog.xml"); if (!xmlResponse.ok) throw new Error(`XML HTTP {xmlResponse.status}`); const xmlText = await xmlResponse.text(); // 2. Load XSLT const xsltResponse = await fetch("products-to-html.xsl"); if (!xsltResponse.ok) throw new Error(`XSLT HTTP ${xsltResponse.status}`); const xsltText = await xsltResponse.text(); // 3. Parse both const xmlParser = new DOMParser(); const xmlDoc = xmlParser.parseFromString(xmlText, "application/xml"); const xsltParser = new DOMParser(); const xsltDoc = xsltParser.parseFromString(xsltText, "application/xml"); // 4. Check for parse errors if (xmlDoc.querySelector("parsererror") || xsltDoc.querySelector("parsererror")) { throw new Error("Parse error in XML or XSLT"); } // 5. Perform transformation const processor = new XSLTProcessor(); processor.importStylesheet(xsltDoc); const resultDocument = processor.transformToFragment(xmlDoc, document); // 6. Display result resultDiv.innerHTML = ""; resultDiv.appendChild(resultDocument); } catch (err) { resultDiv.innerHTML = `<div style="color:red; padding:16px; background:#fee2e2; border-radius:6px;"> Error: ${err.message} </div>`; } } </script> </body> </html> |
Same catalog.xml and products-to-html.xsl as in previous examples
What happens when you click the button?
- JavaScript fetches both files
- Parses them into DOM Documents
- Creates XSLTProcessor
- Imports the stylesheet
- Runs the transformation → gets a DocumentFragment
- Appends the fragment to the page → you see the styled table
5. Common problems & solutions (2025 reality)
| Problem | Symptom / error message | Solution / workaround |
|---|---|---|
| CORS error when loading local files | “Access to fetch blocked by CORS policy” | Run a local server (Live Server, Python -m http.server, XAMPP…) |
| XSLTProcessor not available | XSLTProcessor is not defined | Very rare in 2025 – all major browsers still support it |
| Transformation outputs nothing | Blank result | Check match=”/”, make sure <xsl:apply-templates/> is called |
| Namespaces not handled | Nothing selected | Add namespace declarations or use local-name() |
| Performance with very large XML | Browser freezes or slow | Use streaming (XSLT 3.0 + Saxon-JS) or server-side transform |
| XSLT 2.0/3.0 features not working | xsl:for-each-group not recognized | Browsers only support XSLT 1.0 natively – use Saxon-JS for 3.0 |
Summary – Client-side XSLT in 2025 – Quick Reference
| Method | When to use it | Advantages | Disadvantages / limitations |
|---|---|---|---|
| <?xml-stylesheet?> | Static XML files, demos, offline use | Zero JavaScript, very simple | No error handling, no parameters, no dynamic loading |
| XSLTProcessor + JavaScript | Dynamic loading, error handling, parameters | Full control, can combine with JS logic | More code, CORS issues with local files |
| Saxon-JS (XSLT 3.0 in browser) | Need XSLT 2.0/3.0 features (grouping, JSON…) | Modern XSLT features in browser | Larger library (~400 KB), more complex setup |
Would you like to continue with one of these next?
- Passing parameters to XSLT from JavaScript
- Error handling in client-side XSLT
- Using Saxon-JS for XSLT 3.0 features in browser
- Combining XSLT with modern JS (fetch → transform → render React/Vue)
- Real-world use cases — dynamic invoice preview, RSS feed styling, XML config preview
- Debugging client-side XSLT (DevTools tricks)
Just tell me which direction feels most useful or interesting for you right now! 😊
