Using PUG (or Jade) templates with the Angular-CLI

Mark Pieszak
Oct 1, 2017 · 4 min read

Note: For Angular 6+ you can simply run ng add ng-cli-pug-loader to automatically run & add the scripts below to your project.

Note: The original article was for Angular 5 (and lower / older versions of the Angular CLI) It was more of a temporary/hacky solution until there is more formal support within the Angular-CLI or with Schematics.

[Update 5/29] Angular CLI 6+ PUG Schematics

This is only for the latest CLI 6.0 or higher, but you can run the script ng add ng-cli-pug-loader and it will automatically run and create all the scripts & code that are found below in this article.

ng add ng-cli-pug-loader

Angular <5 (and older Angular-CLI versions <6)

Remember if you’re using the latest CLI you can use the ng add script above, which utilizes the code & scripts below and runs them automatically for you.

If you’ve ever tried to get pug or jade templates working with the Angular CLI, you might have ran into a few random solutions out there, each with their own pros and cons, but nothing super simple.

selector: 'my-thing',
templateUrl: './my-thing.pug' // <-- how can we get this to work?

Some things -not- to do

  • Don’t try to use require, since it won’t be statically analyzable by the Ahead-of-time compiler (AoT).
    template: require('pug-loader!./app.component.pug')()
    I’ve seen a lot of projects do this, and avoiding AoT withng build --aot=false but by doing that you’re including the compiler in your prod builds, and increasing package size.
  • You could use gulp to turn all of your .pug files into .html files (referencing the html file in your components templateUrl), and set up a gulp watch to check pug files into html, but it’s 2017 — it’s… illegal to use Gulp now isn’t it? (kidding of course). All of your templates now have to point to the compiled html file as well.
  • There are also some packages around that transform your pug files into strings within a .pug.ts TypeScript file string, and you import that result. That works great with AoT, but you’ll find ng serve doesn’t work anymore. So that’s a no-go.

What about ng eject?

So another option is that you could ng eject your Webpack configuration from the CLI, and simply add the pug rule yourself. But for a lot of users, people want to stay within the comforts of the CLI.

After ejecting, just look for rules: [ within the ejected webpack.config file, and add this in the rules array.

{ test: /.(pug|jade)$/, loader: "apply-loader!pug-loader?self" },// make sure you install the loaders for your project!
npm i -D apply-loader pug-loader

This would be the most elegant solution if you’re comfortable handling Webpack yourself, and leaving the bounds of the CLI itself.

But if you want keep the CLI, what else can you do?

If you want to stay within the comforts of the Angular CLI, the easiest solution I’ve come up with, and this is one that works with normal JIT compilation as well as AoT, is editing the underlying webpack config from inside the CLI itself (that’s inside your node_modules).

Like I said, this is a bit hacky :)

It’s as easy as adding a new rule within rules: [] (just like above but deep within node_modules ), but who wants to do all that manually!

Script time

Before we get started with that, just install two quick loaders that we’re going to need:

npm i -D apply-loader pug-loader//  ^ shortcut for "npm install --save-dev" here

Now, let’s make a little javascript file, calling it pug-rule-insert.js, and make sure you put it at the Root level of your entire project.

Here we’re just going to be looking for that common.js webpack file within node_modules/@angular/cli, and inserting a little pug test.

Now we can fire this up just by doing a quick node pug-rule-insert.js

Taking it one step further, let’s add a little postinstall hook to our package.json scripts… now we’re real fancy.

Now we get to use our .pug files directly for our templateUrl, and all is right with the world!

selector: 'my-component',
templateUrl: './my-component.pug' // <-- ta-da!


One thing to note, currently include ./my-other-pug isn’t fully working yet, but there’s a super easy work-around for that!


Piece of cake.

The best part?

ng serve && ng build --prod all work, and you even have live-reload / HMR when your doing your local development and updating your pug files.

It works with Universal+CLI projects as well


Find me Online !


Trilon — Next-level Application Consulting

is a software consulting firm aimed to help get teams & applications up to speed by working with some of the leading industry experts in JavaScript, NodeJS, NestJS Framework & ASP.NET.

Contact us:


Trilon Consulting — Next-level Application Consulting

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store