Prettier XML Formatting: Inline/Block Elements & Whitespace
Learn how to format XML with Prettier XML plugin, handling inline vs block elements correctly. Preserve whitespace using xmlWhitespaceSensitivity, prettier-ignore, xml:space, and CI-ready alternatives like xml-formatter or xmllint.
How to format XML files with Prettier XML plugin handling inline and block elements correctly? Prettier does not distinguish block vs inline elements, causing odd formatting. Is there a CI-ready solution or better alternative to specify block/inline elements and preserve whitespace?
Prettier’s XML plugin doesn’t have built‑in knowledge of “inline” vs “block” elements, so you’ll see short tags split across lines or odd indentation when formatting XML with Prettier XML plugin. You can reduce the pain by setting xmlWhitespaceSensitivity to "preserve", using <!-- prettier-ignore-start --> / <!-- prettier-ignore-end --> around problem nodes, or marking inline nodes with xml:space="preserve" and running an XML-aware formatter (or classic tools like xmllint/xmlstarlet) in your CI pipeline. For strict inline/block control the practical choices are: preprocess to tag inline elements, use a dedicated XML formatter that respects xml:space (for example the xml-formatter package), or write a small custom Prettier plugin — the last two are the most reliable for CI.
Contents
- Prettier XML plugin options for XML format and whitespace
- Practical workarounds: xml:space and prettier-ignore
- CI-ready Prettier workflow (checks and autoformat)
- Alternatives: XML formatter tools that respect xml:space and inline/block elements
- How to pick the right approach (tradeoffs & quick recipes)
- Sources
- Conclusion
Prettier XML plugin options for XML format and whitespace
Prettier’s XML plugin treats tags uniformly; it doesn’t have a notion of “this element is inline” versus “this is a block element.” See the plugin repo for details and the available options: the plugin exposes an xmlWhitespaceSensitivity setting with values like "strict", "ignore", and "preserve" that change how whitespace is handled during formatting (prettier/plugin-xml, npm: @prettier/plugin-xml). There’s an active discussion about preserving significant whitespace in XML in issue #478 — worth reading if you need fine-grained behavior: https://github.com/prettier/plugin-xml/issues/478.
Example .prettierrc (project root):
{
"plugins": ["@prettier/plugin-xml"],
"xmlWhitespaceSensitivity": "preserve",
"printWidth": 100
}
CLI (format all XML files):
npx prettier --plugin=@prettier/plugin-xml --write "**/*.xml"
# or the explicit binary:
./node_modules/.bin/prettier --plugin=@prettier/plugin-xml --write "**/*.xml"
If you set xmlWhitespaceSensitivity to "preserve", Prettier will avoid normalizing text-node whitespace, which helps when whitespace inside an element is significant. But note: even with preserve, Prettier still decides line breaks and indentation based on its generic rules — it won’t automatically keep specified tags inline. For that you need a different technique.
Practical workarounds: xml:space and prettier-ignore
Want to keep particular nodes exactly as authored? Two practical workarounds work well in real projects.
- Mark elements that must keep their internal spacing with
xml:space="preserve". The XML spec and common parsers recognize this attribute as the canonical way to indicate that whitespace inside this element is significant. See background onxml:spaceand whitespace handling: http://usingxml.com/Basics/XmlSpace, https://www.tutorialspoint.com/xml/xml_white_spaces.htm and Oracle’s overview https://www.oracle.com/technical-resources/articles/wang-whitespace.html.
Example:
<description xml:space="preserve"> This is significant spacing. </description>
A formatter that respects xml:space will keep that internal spacing intact. The xml-formatter NPM package is one such tool that explicitly respects xml:space and can be used as an alternative to Prettier for XML formatting: https://www.npmjs.com/package/xml-formatter.
- Use Prettier ignore comments around problem nodes when you only have a few exceptions. Prettier supports
<!-- prettier-ignore-start -->/<!-- prettier-ignore-end -->in XML; wrap the section you don’t want Prettier to touch.
<!-- prettier-ignore-start -->
<inline><b> exact spacing </b></inline>
<!-- prettier-ignore-end -->
Caveats: prettier-ignore leaves raw content in the repo (manual markers add noise), and adding xml:space changes the XML to include metadata — acceptable in many schemas, but check compatibility first.
If you have many inline elements, automate adding xml:space="preserve" before formatting via a small preprocessing script (XSLT, xmlstarlet, or a short Node script that parses and updates attributes), then run your formatter. That way the source is consistently annotated and CI-friendly.
CI-ready Prettier workflow (checks and autoformat)
You can make XML formatting part of CI either with Prettier (best for teams already standardizing on Prettier) or with XML-specific tools.
Prettier-based CI (fast, consistent with JS/HTML formatting steps):
- Add
@prettier/plugin-xmlto devDependencies. - Add scripts to package.json:
{
"scripts": {
"format:xml": "prettier --plugin=@prettier/plugin-xml --write \"**/*.xml\"",
"check:xml": "prettier --plugin=@prettier/plugin-xml --check \"**/*.xml\""
}
}
- GitHub Actions job (example):
name: Check XML formatting
on: [push, pull_request]
jobs:
xml-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with: { node-version: 18 }
- run: npm ci
- run: npm run check:xml
If Prettier can’t express the inline/block policy you need, replace or augment the check with an XML-specific formatter or validator.
XML-tooling CI (deterministic formatting or validation):
- Use
xmllintorxmlstarletto pretty-print and compare. These classic tools are easy to install on CI runners and scale to large files; see practical CI tips: https://encode64.com/en/minifiers/xml-minifier. - Example quick check (bash):
# format to a temp file and diff
xmllint --format file.xml > file.formatted.xml
if ! diff -q file.xml file.formatted.xml; then
echo "file.xml is not formatted"
exit 1
fi
This approach gives you deterministic XML-only behavior and avoids fighting Prettier’s generic tag wrapping.
Alternatives: XML formatter tools that respect xml:space and inline/block elements
If you need precise control over how inline tags are kept on one line or block-level tags are expanded, consider dedicated XML formatters rather than Prettier.
-
xml-formatter(npm) — a focused XML pretty-printer that respectsxml:spaceand is easy to script into CI or pre-commit hooks: https://www.npmjs.com/package/xml-formatter. Use it programmatically or call it from a Node script to format files consistently. -
Classic utilities —
xmllintandxmlstarletare battle-tested for formatting and processing large XML files; they integrate well into CI and can be combined with shell scripts for batch processing: https://encode64.com/en/minifiers/xml-minifier. -
Online tools — handy for one-off manual fixes (jsonformatter.org, onlinexmltools), but not for CI: https://jsonformatter.org/xml-formatter, https://onlinexmltools.com/prettify-xml.
If none of these meet your exact inline/block rule needs, you can write a small formatter/script that:
- Parses XML,
- Maps a configured list of inline tags,
- Emits those inline tags on a single line (or sets
xml:space="preserve"), - Then runs an XML pretty-printer for the rest.
That gives you full control without rewriting a Prettier printer.
How to pick the right approach (tradeoffs & quick recipes)
Which route should you take? Quick decision guide:
-
You want minimal changes and already use Prettier across the repo:
-
Try
xmlWhitespaceSensitivity: "preserve"+prettier-ignorefor a few spots. -
Add
check:xmlandformat:xmlscripts and enforce them in CI. -
You need consistent, project-wide inline/block rules (many inline tags):
-
Add
xml:space="preserve"to those tags automatically (preprocess) and use an XML-aware formatter (or xmllint) in CI. -
Or choose a dedicated formatter like
xml-formatterand run it in CI and pre-commit hooks. -
You need exact, deterministic output (e.g., for generated config files or tests):
-
Use classic tools (
xmllint/xmlstarlet) or a dedicated XML formatter in CI. Don’t rely on Prettier for this level of control.
A sample practical recipe I use often:
- Maintain a JSON list of inline tags in repo (e.g., [“b”,“i”,“code”,“abbr”]).
- Run a small Node script that adds
xml:space="preserve"to those tags (idempotent). - Run
xml-formatter(or xmllint) to pretty-print. - Commit formatted files; enforce with a CI
prettier --checkornode format-check.js.
That keeps behavior deterministic, CI-friendly, and avoids littering the source with prettier-ignore comments.
Sources
- https://github.com/prettier/plugin-xml
- https://github.com/prettier/plugin-xml/issues/478
- https://jsonformatter.org/xml-formatter
- https://onlinexmltools.com/prettify-xml
- https://www.npmjs.com/package/@prettier/plugin-xml
- https://salesforce.stackexchange.com/questions/415949/how-can-i-get-prettier-to-properly-format-perm-set-xml-files-with-flowaccess
- https://www.freecodecamp.org/news/alternatives-to-prettier/
- https://dev.to/withtoms/prettier-and-how-to-get-most-out-of-it-2d46
- http://usingxml.com/Basics/XmlSpace
- https://www.oracle.com/technical-resources/articles/wang-whitespace.html
- https://encode64.com/en/minifiers/xml-minifier
- https://www.npmjs.com/package/xml-formatter
- https://www.tutorialspoint.com/xml/xml_white_spaces.htm
Conclusion
Prettier XML plugin is convenient but won’t natively honor inline vs block semantics — that’s why short inline tags can look wrong after formatting. For CI-ready XML format control, either (a) use the plugin with xmlWhitespaceSensitivity: "preserve" plus targeted prettier-ignore and xml:space="preserve" annotations, or (b) switch to an XML-aware formatter (like xml-formatter) or classic tools (xmllint/xmlstarlet) run from CI. If you need full, deterministic inline/block rules, preprocess tags or use a dedicated XML formatter (or build a custom Prettier plugin) — the result is predictable XML format that preserves whitespace the way you expect.