Pug to HTML
Convert Pug (formerly Jade) templates to HTML by Pug's own rules — tags, classes, IDs, attributes, text blocks and doctypes, with the error Pug would report.
What is Pug (formerly Jade)?
Pug is a template engine for Node.js, inspired by HAML. It eliminates closing tags and angle brackets — h1.title Hello becomes <h1 class="title">Hello</h1>. This converter follows the rules of Pug itself, version 3, ported from its lexer, parser and code generator: tags, classes, IDs, attributes across several lines, block expansion, text blocks, comments and doctypes, indented as Pug's pretty option writes them or compact, as Pug renders by default. It reports the first error Pug would raise, with its line. JavaScript can't run here, so code, conditionals, loops and mixin calls are shown as HTML comments where their output would go, and a note lists each line that needed it.
Built and maintained by Meet Shah · Last updated
What this tool is used for
- Understanding an existing Pug template by seeing the markup it produces.
- Migrating a template off Pug onto plain HTML.
- Debugging nesting where indentation has gone wrong.
- Checking what an attribute list with class arrays, style objects or false values writes.
- Checking what a Pug shorthand actually generates.
- Finding the line Pug would reject, and its error message, before a build fails on it.
How it works in practice
A worked example
A developer is moving a newsletter signup form out of an Express app's Pug views onto a static landing page, and needs the plain HTML the snippet stands for.
//- signup form, included in the blog sidebar
form.signup#newsletter(action="/subscribe", method="post")
// v2 form, see ticket WEB-118
label(for="email") Email
input#email(type="email", name="email", required)
label
input(type="checkbox", name="weekly", checked)
| Weekly digest
button.btn.btn-primary(type="submit") Subscribe<form class="signup" id="newsletter" action="/subscribe" method="post">
<!-- v2 form, see ticket WEB-118-->
<label for="email">Email</label>
<input id="email" type="email" name="email" required="required"/>
<label>
<input type="checkbox" name="weekly" checked="checked"/>Weekly digest
</label>
<button class="btn btn-primary" type="submit">Subscribe</button>
</form>The two comments went different ways. The //- line is gone, because Pug keeps that form for notes to template authors, while the // line became an HTML comment holding its text exactly as typed, a space after <!-- and none before -->, so the ticket reference now ships in the page source. Each shorthand became attributes, the class first and the rest in source order, as Pug writes them, and the indentation became closing tags. Rendered on its own, with no doctype html, Pug writes required="required" and checked="checked" and closes each input with a slash; included in a page that starts with doctype html, the same lines come out as bare required and checked, with no slash. Weekly digest shares the checkbox's line because Pug's pretty output starts a line only for block tags and writes text where it falls.
The edge case that catches people
Attribute values in Pug are JavaScript, and this converter works out only the constant ones, as Pug does at compile time: checked=false drops the attribute, and class=['btn', {active: true}] becomes class="btn active". Anything else is copied as written, with a note naming its line. So input(type="checkbox", name="weekly", checked=user.weekly) comes out with checked="user.weekly", and because HTML treats a boolean attribute as true whenever it is present, whatever its value, that box would render ticked for every visitor, where Pug leaves the attribute out when the value is false. The same copying turns href=profileUrl into a link to a page called profileUrl. Check each line the note lists by hand.
When not to use this tool
Don't use it to migrate a folder of Express views. Real templates lean on extends, include, mixins and each, and resolving them takes the other template files and the data the route passes in. Here, include, extends, loops and mixin calls become HTML comments holding the original Pug, a block writes only its default content, and every branch of a condition and the body of each loop come out once, as if they were static markup. Render each view with Pug itself, through pug.renderFile and a set of sample locals, and convert or diff the HTML that comes back; keep this page for reading a fragment or checking what a piece of shorthand expands to.
Frequently Asked Questions
- How does Pug express nesting?
- By indentation, with no closing tags at all: a child is indented under its parent, or follows it after a colon on the same line, as in
li: a(href="/") Home. That makes the structure visible in the template's shape, and makes a misplaced indent a structural change rather than a cosmetic one. - What is the shorthand for classes and IDs?
- CSS-style:
h1.hero#mainis<h1 class="hero" id="main">. A bare.cardwith no tag name defaults to a div, which is why idiomatic Pug contains almost no explicit divs and reads much closer to a stylesheet than to HTML. - Why was it renamed from Jade?
- "Jade" turned out to be a registered trademark, so in 2016 the project became Pug. The rename came with version 2, which removed some Jade syntax:
#{}inside an attribute value, for one, is now written out literally. The jade package on npm still installs, but it is deprecated and stopped at 1.11. - How is interpolation written?
#{expression}inside text, and= expressionfor an element's entire content. The distinction matters for escaping: both escape HTML by default, and!=is the explicitly unescaped form — which is the one to audit whenever user data is involved.- Is Pug still worth using?
- In existing Express and Node projects, yes, and the terseness is genuine. New work has largely moved to component frameworks where markup and logic are colocated, so converting Pug to HTML is most often the first step in migrating away from it.
- Why does a link sit on the same line as the tag before it?
- Pug's pretty output starts a new line only for block tags. Inline tags such as a, span, strong and img, and plain text, are written where they fall, so a link after a list comes out as
</ul><a href="/">Docs</a>, exactly as Pug writes it. Wrap the link in a block tag such as p to give it its own line. - Why is the output empty?
- Pug compiles nothing from a template it cannot read, so neither does this page. The message under the input is the first error Pug would raise, with its line, such as tabs mixed with spaces, a bracket left open or content nested under img. Fix that line and the HTML comes back.
- Should I use the pretty or the compact output?
- Compact is what Pug writes by default, and what a server sends. Pretty is easier to read, but Pug's own docs deprecate the option: the line breaks it adds are whitespace, which can change rendering, such as a gap appearing between inline-block elements that compact output keeps touching.
Common errors and gotchas
- Mixing tabs and spaces, which Pug treats as a structural error.
- Expecting interpolation and mixins to resolve, which need the runtime rather than a static conversion.
- Losing filters and included files, which are resolved at build time.
- Expecting a dot block's text to be escaped, when Pug writes it as typed, so a < in it reaches the HTML unchanged.
- Assuming the output is the final markup, when a layout template usually wraps it.
- Writing text on the line under a tag without a pipe or a dot, which Pug reads as another tag.