Wisdom Ekpot A student of Ibom Metropolitan Polytechnic studying computer engineering, Wisdom has been writing JavaScript for two years, focusing on Vue.js, Angular, and Express.js.

Pug.js tutorial: A beginner’s guide with examples

5 min read 1496

Using Pug.js With Vue.js

In this Pug.js tutorial, we’ll show you how to integrate Vue.js into a backend application using Pug as a templating engine.

For an introduction to Pug.js, check out “Getting started with Pug.”

What is Pug.js?

Pug.js, formerly known as Jade, is a templating engine designed to render HTML in server-side technologies such as Node.js. Like any JavaScript templating engine, Pug.js supports writing reusable HTML code and rendering dynamic data.

Vue.js is a progressive framework for JavaScript that is used to build web interfaces and single-page applications. Not just for web interfaces, Vue.js is also used both for desktop and mobile app development with the Electron framework.

Why use Pug with Vue.js?

Most backend developers choose to use Pug with Vue.js because it’s much easier to implement and read and doesn’t require a whole lot of configuration. Any valid HTML is also a valid Vue.js template. Using Pug.js as a preprocessor for your Vue.js templates makes it much easier to migrate existing projects to take advantage of Vue’s reactivity features.

Pug.js is frequently used with backend technologies such as Express.js for templating since it compiles down to HTML. Unlike backend developers, most frontend developers find Pug frustrating to write in and maintain because it’s white space-sensitive, which means it uses indentation to determine which tags are nested inside each other.

To follow along with this tutorial, you should have an understanding of JavaScript and Vue.js and experience using a text editor, such as VS Code.

Setting up a Node.js project

When setting up a nodejs project, we need to initialize a package.json file to keep track of our application dependencies using the npm init -y command.

Next, we’ll create an index.js file, which will be our root Node.js file. We’ll use Express.js for our server, so we need to install Express, import it into our root index.js file, and create an instance of it. We’ll use the instance to listen to a port running on 3000.

Installing Express.js in a Node.js app

To install Express.js, open up your terminal and type npm i express --``save.

let express = require('express');
let app = express();

app.get('/', (req, res) => res.json({msg:"Hello World"}))

app.listen(3000, () => console.log('Test running'))

To run the application, open your terminal and type node index.js. This command will display Test running on the terminal, which means the server is running. Now we can access our application on http://localhost:3000/ in our browser:

Pug.js: 'Hello World'

Setting up Pug.js

To set up Pug as your templating engine, first install it by running npm install pug.

After installing Pug, we need to set it as the default Express templating engine and also point to a directory where all our templates will be defined. We can modify our index.js file to this:

let express = require('express');
let pug = require("pug")
let path = require('path')
let app = express();

app.set(path.join(__dirname, './views'))
app.set('view engine', 'pug')

app.get('/', (req, res) => res.render('home'))

app.listen(3000, () => console.log('Test running'))

Notice we required the Node.js path module, which provides utilities for working with file and directory paths.

Let’s create a views directory where we’ll define all our Pug templates. We’ll use the path module to target the views directory and set it as the root directory for all our templates.

Next, create a home.pug file inside the views directory and add this:

    h1 Hello World

Our application will now render Hello World in the browser because we changed the route to render the home.pug template. Notice how Pug uses indentation to work out which tags are nested inside each other.

Using Pug with Vue.js

To set up Vue.js in our Pug template, we need to install and set up webpack.

webpack is basically a module bundler. Its main purpose is to bundle JavaScript files for use in a browser.

To install webpack and its CLI, open up the terminal and type the following:

npm i -D webpack webpack-cli

After installing webpack, we need to install some other packages to help bootstrap Vue.js:

  • vue-loader, a loader for webpack that allows you to set up Vue.js components as a single-file component
  • vue-loader-plugin, which helps solve compatibility issues with Webpack5 configurations.
  • vue-template-compiler, which is used to precompile Vue.js 2.0 templates into render functions to avoid runtime-compilation overhead and CSP restrictions.
  • css-loader, which interprets and resolves @import and url() like import/require()

To install these packages, open up your terminal and type the following:

npm i -D vue-loader vue-loader-plugin vue-template-compiler css-loader

The -D flag will install these packages as a development dependency.

Next, we have to install babel, a toolchain that is mainly used to convert ECMAScript 2015+ code into a backward-compatible version of JavaScript in current and older browsers and environments. With this installed, we can use things like import and export in our JavaScript file.

We’ll start by installing babel-watch using npm by running npm i babel-watch on our terminal and then installing babel-core and babel-loader as a development dependency:

npm i -D babel-core babel-loader

Let’s create a client directory where we’ll write our Vuejs codes. Inside the directory, create a home.js file and add the following:

import Vue from "vue";
let app = new Vue({
    el: '#home',
    data: {
        names: ['Wisdom', "Ekpot", "Ubongabasi"]
    methods: {
        logSomeThing() {
            return 'Hello Wisdom Ekpot'
    mounted() {

If you’re familiar with Vue.js, you should recognize this as a simple Vuejs setup. Here, the root element is targeting a div with an id of home. Once the template is mounted, the logSomething method is called, which is defined in the methods object.

To include this in our Pug file, we’ll have to add some webpack configs. Let’s create a webpack.config.js file, import webpack, and write some configs for Vue.js.

We’ll start by importing webpack, path, and VueLoaderPlugin:

const webpack = require('webpack');
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

Our webpack config will be wrapped in a config object. We’ll start by setting the mode to development and also define our entry file.

After setting an entry file, we need to set an output where all the transcompiled code will be stored. The output property tells webpack where to emit the bundles it creates and how to name these files. Basically, webpack takes our Vue.js code and transcompiles it to vanilla JavaScript.

const config = {
    mode: 'development',
    entry: {
        'home': './client/home.js',
    output: {
        path: path.resolve(__dirname, 'public/js'),
        filename: '[name].bundle.js'

We’ll create a public directory to store our compiled code. We also need to come up with some webpack rules to be used when the rule matches. These rules are evaluated only when the parent rule condition matches. Each nested rule can contain its own conditions.

 module: {
        rules: [
                test: /\.vue$/,
                loader: 'vue-loader'
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
                test: /\.css$/,
                use: [

Basically, webpack checks if there is any .vue file extension and uses the vue-loader plugin to bundle it to JavaScript.

We also need to define a resolve object to configure how modules are resolved:

 resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        extensions: [

Next, we need to define a new instance of VueLoaderPlugin to help handle some configs for Webpack 5:

 plugins: [
        new VueLoaderPlugin(),

After doing this, we have to export our config object:

module.exports = config;

To bootstrap this webpack configuration, we need to add some scripts in our package.json file to bootstrap it:

 "watch": "babel-watch  --watch -- index",
    "dev": "npm run watch & npm run client:build-dev ",
    "client:build-dev": "webpack --watch"

The client:build-dev command will start Bootstrap and watch our files for any changes. To start the application, run npm run dev on you terminal.

Now we can modify our home.pug file to this:

    h1 Hello World

If we run npm run dev on our terminal, it creates a home.bundle.js file inside our public directory, which is our transcompiled code.

If we look at our browser console, we’ll see the logSomeThing() method printed:

Vue.js in a Pug Template

The above indicates that Vue.js is being used in our Pug template.

The last thing we have to do is create a .gitignore file and add the following:


This will prevent the node_modules directory and the public directory from being committed in git.


When working with large and complex frontend tasks on your backend, using Pug with Vue.js is a great way to simplify and scale the development process.

The source code for the example used in this article is available on GitHub.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Wisdom Ekpot A student of Ibom Metropolitan Polytechnic studying computer engineering, Wisdom has been writing JavaScript for two years, focusing on Vue.js, Angular, and Express.js.

4 Replies to “Pug.js tutorial: A beginner’s guide with examples”

  1. Who still implement express when it’s natively supported with vue cli or nuxt ? And vite / snowpack.

  2. I have just made `pug-vue-loader`, a replacement for `pug-plain-loader` that allows you to use _proper_ pug syntax in your Vue pug templates, to access Vue state in native pug `each`/`for` blocks, as well as `if`/`else` etc… and also use proper pug variable interpolation eg `li= someVarFromVue` or `li normal text #{someVarFromVue}`

    If you like real pug syntax and want to use its first-class loops/conditionals etc in your Vue templates, check it out:


    As an example, instead of this:


    li(v-for=”item in items”)
    a(v-if=”item.type == ‘link'” :href=”item.url”) some link title: {{item.title}}
    p(v-else) {{item.content}}

    // …Vue component JS

    The `pug-vue-loader` lets you do this:


    for item in items
    if item.type == ‘link’
    a(:href=”item.url”) some link title: #{item.title}
    p= item.content

    // …Vue component JS

    Since it’s new, would like to get people using it, reporting any issues etc. Cheers

  3. this tutorial doesnt run. First you get errors that babel core was installing the wrong version, then you get vue not found in node_modules so the import would fail when you try to build. After I fix those I get a 404 error when the home.pug tries to load the script.

Leave a Reply