Why Angular Teams Are Adding Pug To Their Toolchain
Angular’s CLI provides first-class support for CSS preprocessors like Sass/SCSS, LESS, and Stylus, but when it comes to component templates, developers are limited to plain HTML and SVG. That leaves a gap for template engines such as Pug, which can reduce repetition and improve readability in larger view files.
Pug (formerly Jade) is a template engine that compiles templates into functions that render HTML documents. It drops angle brackets and closing tags, relying on indentation to define structure. The result is less code to write and read. For example, an HTML table that requires opening and closing tags for every row and cell becomes a more compact Pug block with nested indentation — seven fewer lines in a small example, with the gap growing as templates get larger.
Beyond cleaner syntax, Pug includes features that Angular templates don’t natively offer: mixins for reusable markup, include for partials, conditionals, iterators, interpolation, and even filters for embedding other languages like Markdown. These can solve problems that might otherwise require separate components, directives, or dependencies.
Setting Up Pug In Angular CLI Projects
For Angular CLI 6 and above, both new and existing projects need the ng-cli-pug-loader package. This loader compiles .pug files referenced in component decorators.
Adding Pug To New Components
- Install
ng-cli-pug-loaderwith your package manager. - Generate a component as usual. For a home page, that might be
ng generate component home. - Rename the generated
.htmlfile to.pug. The initial HTML contents can stay — valid HTML still works in Pug — or be replaced with Pug syntax. - Update the component decorator’s
templateUrlto point to the.pugfile.
Converting Existing Projects
- Install
ng-cli-pug-loader. - Install the
html2pugCLI tool for converting existing HTML templates. - Run the converter with the
-fflag so it doesn’t wrap output inhtmlandbodytags. Use-cto add commas between element attributes — a step that matters for Angular bindings (explained below). - Update each component decorator’s
templateUrlfrom.htmlto.pug. - Run the dev server and compare the rendered output against the original HTML. Indentation errors or unquoted attributes are the usual culprits if something breaks. Once verified, delete the old HTML files.
Angular Syntax Rules Inside Pug Templates
Most Angular template syntax carries over unchanged. The exceptions are bindings and certain directives whose punctuation ((), [], [()]) interferes with Pug compilation. These must be enclosed in quotes, or separated with commas when there are multiple attributes. The -c flag on html2pug handles multi-attribute elements automatically; for single attributes, add quotes manually.
Specifically, wrap these in quotes:
- Event bindings and property bindings, e.g.
(click),[src],[(ngModel)] - Attribute directives such as
ngClass,ngStyle, andngModel - Structural directives like
*ngIf,*ngFor,*ngSwitchCase, and*ngSwitchDefault
Template reference variables (e.g. #var) and interpolation with {{ }} work without any special handling.
Limitations And Trade-offs
Using Pug through ng-cli-pug-loader comes with constraints that are worth knowing before committing to a migration.
Partial includes are restricted. External files referenced with include must end in .partial.pug, .include.pug, or be named mixins.pug. Template inheritance — Pug’s block, append, and prepend features — does not work with this loader.
Inline templates are not supported. The loader only renders template files. Any inline HTML in component decorators must first be extracted to an external file, then converted.
Angular and Pug contexts are separate. Pug templates don’t have access to component properties as Pug variables. You can’t use a component field inside a Pug conditional, iterator, or inline script. Conversely, Angular template expressions can’t read Pug variables. They share the rendered DOM, but not a variable scope.
CLI workflow needs manual steps. Angular CLI generates .html files, so each new component requires a rename or manual file creation plus a decorator edit. This can be scripted or automated with a custom Angular schematic, but the setup is on you.
Migration cost on large projects. Converting an entire existing codebase means touching every component. Bindings and directives need quoting or commas, and unfamiliarity with Pug’s indentation-based syntax adds to the learning curve.
Finally, index.html cannot be converted to Pug — the loader doesn’t handle the app shell.
When Pug Makes Sense For Angular
Pug is a genuine productivity boost for Angular templates, offering a tighter syntax and features that close the gap between what Angular’s template language provides and what developers need. The integration path is straightforward for small or new projects. Larger existing codebases face a more manual migration, and the loader’s limitations around includes, inheritance, and variable scoping mean you should evaluate how much of Pug’s feature set you’ll actually be able to use.
For teams willing to adopt its syntax and adjust their workflow, Pug can make Angular templates noticeably more maintainable. The full details on Pug’s capabilities are available in the official documentation, and the loader’s source is open for those who want to build a tailored Angular schematic around it.



