Node.js – building a simple website using Pug and Express

Last week I took a look at using Node.js to build a simple file uploader as a bit of an introduction to the framework. Since then I’ve been taking a bit of a deeper dive into Node, looking at how I can use different packages to do different, fun things such as running sites in a production environment. One of the things I REALLY wanted to take a look at, though, was how to template the front end of Node apps to make them easier to work with. Enter Pug!

Pug is a Node templating language which takes the fundamentals of HTML and breaks things down into an easy to work with environment with fun things like mixins and code blocks. There’s a number of ways you can use Pug – ultimately we’ll be mixing it up with Express to deliver pages over a server but, for now, we’re going to look at Pug on a fundamental level and use it to generate flat HTML pages.

Step 1: Spin up a new project and install Pug

You know what to do! Go create a new project folder and initialise a Node project in it – if you don’t know what you’re doing with that then check out my first post on Node right here.

The next thing we’re going to do is install a couple of packages to help us with Pug. First off, Pug CLI, a command line tool that will let us do stuff with Pug files:

npm install pug-cli -g

That will make sure that Pug CLI is installed globally and available to all your Node projects. Next off, install Pug into your project.

npm install --save pug

That will grab Pug and install it into your Node project – you’re now ready to create your first Pug file!

Step 2: Create a Pug file

Okay, here’s where things get fun. Create a folder in your project called “views” – this is where we’ll be putting our Pug files. Let’s create one in there now called index.pug. If you’re using VS Code, then there’s some helper extensions you can look at to get formatting and intellisense into your editor for this extension. Here’s some example code for you:

doctype html
html(lang="en")
   head
       meta(charset="UTF-8")
       meta(name="viewport" content="width=device-width, initial-scale=1.0")
       meta(http-equiv="X-UA-Compatible" content="ie=edge")
       link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css")
       link(rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css")
       title
           | Node.js Powered Site
   body
       div.container
           div.row
               div.col
                   h1
                       | Node.js Powered Site example
           div.row
               div.col
                   p
                       | A quick and dirty site showing how you can use Express, Pug and Bootstrap to write flat site content.
       script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js")
       script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.13.0/umd/popper.min.js")
     script(src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.min.js")

So, yes, that looks very much like HTML but without the <> in place. That’s Pug in a nutshell. It’s quite a fluid language in a number of ways but there are some hard and fast rules you need to follow.

First off, indentation is important. This tells Pug how elements are nested so if you don’t indent correctly then it’ll throw errors when rendering pages.

Secondly, class and id attributes are handled using . and # notation respectively, so, div.row is a div with a class of row, where div#row is a div with an id of row. These can be mixed and matched, for example, div.row#comments.

Lastly, other attributes are included in the () after the element type. You’ll see here that this covers things like src, href etc.

For content within tags, such as the h1 and p tags you can see here, there are a number of different approaches, but my personal preference is to add them on a new line, indented, with a | at the start of the content.

You can read up more on Pug here: https://pugjs.org/api/getting-started.html

For now, save this page.

Step 3: Render the HTML

So, you’ve got your nicely formatted Pug file – that’s great but no browser is going to be able to handle that natively, so we’re going to need to get that converted to HTML. Remember you installed Pug CLI? Let’s use that. Head to your terminal and in the root of your project type:

pug views --out ./

Hit return and Pug will generate an index.html file in the root of your project. Nifty, no? So what’s happening here? The command above is simply telling Pug to render every .pug file in the “views” folder and output the resulting HTML files to the project root. Obviously you can substitute the folder of your choice for ./, be it “html” or “dist

That’s really the basics of Pug, but I’m going to go one further and look at how we can use mixins and blocks to break our Pug files up into more logical template structures.

Step 4: Build a reusable template

One of the cardinal rules of coding is not to repeat yourself – build functions, variables and templates so that you can do things once and then reuse them. Pug gives you the tools to do this via mixins and blocks and we’ll use both of these to build our reusable template.

First off, let’s create our basic page structure that we’ll reuse across all of our pages. Create a new folder in views and call it “layout”. In your layout folder, create a file called layout.pug with the following code:

doctype html
html(lang="en")
   head
       meta(charset="UTF-8")
       meta(name="viewport" content="width=device-width, initial-scale=1.0")
       meta(http-equiv="X-UA-Compatible" content="ie=edge")
       link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css")
       link(rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css")
       title
           | Node.js Powered Site
   body
       header.container
           div.row
               div.col
                   h1
                       | Node.js Powered Site example
       div.container
           block content
       script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js")
       script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.13.0/umd/popper.min.js")
       script(src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.min.js")

You’ll notice that this is almost identical to our index.pug file, although we’ve now defined a header area and a separate container div which contains the line “block content”. This is a placeholder that we’ll inject our page content into. Head back to your index.pug file and replace everything in it with the following:

extends layout/layout.pug

block content
   div.row
       div.col
           p
               | A quick and dirty site showing how you can use Express, Pug and Bootstrap to write flat site content.

What we’re saying here is that our index.pug file now is an extension of our main layout. When Pug renders the index.html it’ll use this as the basis, injecting the content block into the layout.pug files placeholder. Easy. Let’s build a menu now – we’re going to use a mixin to define the menu items and make it easier to code.

Add this into layout.pug, as a second row in the header container (don’t forget your indentation!):

nav.row
     div.col
           ul.nav.nav-pills

And now create a new subfolder called “mixins” in your views. In here, add a new file called “nav-item.pug” with the following code:

mixin nav-item(title,link)
   li.nav-item
       a(href=link).nav-link
           | #{title}

What this is doing is creating a reusable block of code called a mixin which we can call from our main layout template, defining the attributes for title and link. Let’s see what that looks like.

At the top of layout.pug, include the following:

include ../mixins/nav-item.pug

And under the nav ul, add:

+nav-item('Home','/')
+nav-item('About','about')
+nav-item('Meet The Team', 'team')

When the page renders, those +nav items will be replaced with their respective tags from the mixin file, creating our menu! You can run

pug views --out ./

if you want, but one thing to note here is that there doesn’t seem to be a way to prevent Pug from rendering EVERYTHING in the views folder, including the layout and mixin templates! But, look at the generated index.html file and you’ll see that the page has been created as we would expect, complete with content and menus. Feel free to create another page, perhaps about.pug, in the views folder to see how that also gets created. Proper magic!

Part 5: Use Express to deliver the site

So this is all well and good for generating HTML pages, but what if we want to run our Pug pages as a Node app? Easy – use Express. You’ll remember we did this for our file uploader, so go ahead and install Express into your project:

npm install --save express

TOP TIP: If you’re starting a new project, you can install multiple packages off the same command, for example:

npm install --save pug express

Anyway, now you’ve got Express installed, let’s create a js file – call it nodesite,js or something similar. Pop the following code in it:

var express = require('express');
var site = express();
var port = 3000;

site.set('view engine', 'pug');

site.get('/', function(req, res){
   res.render('index');
});

site.listen(port, () => console.log(`App listening on port ${port}`))

That should be familiar to you with some exceptions. Site.set is telling our app to use Pug as its rendering engine. Express will expect to find our pug files in the “views” folder, which is why we set that up earlier. Res.render tells the app which template to render. At this point you can do all sorts of cool stuff like inject attributes and arrays to include – we may go over that in the future but I’m aware this article is getting loooooong!

So, launch your app with:

node nodesite.js

and head over there with your browser (http://127.0.0.1:3000/).

Et voila! Express is rendering your Pug files on the fly. You’ve just built yourself a simple Node powered site. Try creating different pages and using Express to route to them.

As this example spans a whole bunch of files, I’ve thrown everything up into a GitHub repository so that you can download and try it out for yourself – just head here: https://github.com/abeeken/nodesite

As always, please feel free to chime up in the comments if you have any thoughts!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s