Write templates in Node.js with EJS (Embedded JavaScript)

Sjlouji
JavaScript in Plain English
6 min readSep 14, 2020

--

Cover Image

Template Engine is the presentation layer that actually renders our data in HTML format. By default, express provides us a function to send HTML files. Using the function we can send only static pages. If we need to inject any data in an HTML file, we need a template engine. Also, Templating engines split our code into multiple components like header, footer, body, and so on. So we can reuse any component in any layout. This makes our application more scalable and helps us to create an HTML template with minimal code.

For NodeJS, there are many template engines like Pug, Haml.js, EJS, React, hbs, Squirrelly, and so on. In this blog, I am explaining EJS which is one of the most used template engines.

What is EJS?

EJS is one of the template engines used with Node JS to generate HTML markups with plain Javascript. EJS stands for Embedded JavaScript Templates. It can be used both on the client and the server-side.

1. Creating a Node.js Project

Create a new directory and initialize node with the command npm init.

mkdir expresstemplate
cd expresstemplate/
npm init -y
npm i nodemon

Express JS is an open-source web framework for node JS. The below command installs express to our project.

npm install express --save

package.json

"scripts": {
"start": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},

index.js

const express = require('express')
var app = express()
app.get('/', function(req,res){
res.send('Hello world')
})
app.listen(8000,function(){
console.log('Listening to PORT 8000')
})

2. Rendering an HTML file

Initially let’s render a static HTML template. I am creating a simple bootstrap HTML page named as index.html in the project’s root directory.

index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<title>Template Enginer Node JS</title>
</head>
<body>
<div class='container' style="margin-top: 100px">
<div class="hero-unit">
<h1>Bootstrap</h1>
<p>
<a class="btn btn-primary btn-large">
SignIn
</a>
</p>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> </body>
</html>

Now, create a route /hello which sends our index.html file as a response to the client.

index.js

const express = require('express')
var app = express()
app.get('/', function(req,res){
res.send('Hello world')
})
app.get('/hello', function(req,res){
res.sendFile(__dirname + '/index.html')
})
app.listen(8000,function(){
console.log('Listening to PORT 8000')
})

Once done, visit http://localhost:8000/ in the browser. You should see a page like this.

Sending Response as a File

We can use this when we are handling a static file. In the case of dynamic layouts, this will not be suitable since we can't inject any values to the HTML page. This is the reason why we are moving towards EJS or other Template Engines.

3. Rendering EJS template

As said earlier, EJS is one of the most commonly used template Engines that work on both the server and client-side.

Basic Syntax

a. Unbuffered code for conditionals <% code %> — This syntax is used to handle conditions. The syntax is commonly used for rendering a value or a template conditionally.

<% if (user) { %>
//ToDo: Perform something if user exist
<% } else { %>
//ToDo: Perform something if user does not exist
<% } %>

b. Escapes HTML by default <%= code %> — This syntax is used to render value.

<h2><%= user.name %></h2>

c. Unescaped buffering <%- code %> — This syntax is used when we want to include an external template.

<%- include('../views/profile')-%>

The installation and setup of EJS is too simple. Use the below command to install EJS for your project.

npm install ejs

After installation, create a folder named views in the root directory. After creating it, add the below line in the server file. In my case, it is the index.js file. The below line tells express that my templates are kept under the folder views.

index.js

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs')

Just for the demonstration, I am creating a simple HTML page with an extension ejs.

views/hello.ejs

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<title>Template Enginer Node JS</title>
</head>
<body>
<div class='container' style="margin-top: 100px">
<div class="hero-unit">
<h1>Welcome <%= param %></h1>
<p>
<a class="btn btn-primary btn-large">
SignUp
</a>
</p>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script></body>
</html>

In the server file, I am adding a new route, that renders my hello.ejs file. With that file, I am sending some parameters. This parameter will be received in the name of param. So by wrapping param with EJS syntax, it returns me the parameter sent from the route.

index.js

const express = require('express')
var app = express()
app.get('/', function(req,res){
res.send('Hello world')
})
app.get('/hello', function(req,res){
res.sendFile(__dirname + '/index.html')
})
app.get('/hello/:name',function(req,res){
res.render('hello',{param: req.params.name})
})
app.listen(8000,function(){
console.log('Listening to PORT 8000')
})

When I navigate to http://localhost:8000/hello/louji, It returns me Welcome Louji. If you navigate to http://localhost:8000/hello/medium, it will return Welcome Medium.

Dynamically rendering params

4. Including Templates

Apart from injecting values into a template, EJS also helps us to reuse HTML components.

Below, I have two files named header.ejs and footer.ejs. I am creating both the templates inside the views folder.

views/header.ejs

<html lang="en">
<body>
<header>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" href="#">Carousel</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
</header>
</body>
</html>

views/footer.ejs

<html lang="en">
<body>
<footer class="container">
<p class="float-right"><a href="#">Back to top</a></p>
<p>&copy; 2017-2018 Company, Inc. &middot; <a href="#">Privacy</a> &middot; <a href="#">Terms</a></p>
</footer>
</body>
</html>

In my hello.ejs file, I am using something called include, Which includes the header.ejs and footer.ejs in the hello.ejs file. Likewise, we can reuse the same header and footer in any layouts.

views/hello.ejs

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<title>Template works</title>
</head>
<body>
<%- include('../views/header.ejs')-%>
<div class='container' style="margin-top: 100px">
<div class="hero-unit">
<h1>Welcome <%= param %></h1>
<p>
<a class="btn btn-primary btn-large">
SignUp
</a>
</p>
</div>
</div>
<%- include('../views/footer.ejs')-%>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
</body>
</html>
Including Template

Do check my previous blog, where I have explained about Models and Database

Feel free to contact me for any queries. Email: sjlouji10@gmail.com. Linkedin: https://www.linkedin.com/in/sjlouji/

Complete code on my GitHub:

Happy coding!

--

--

Software Engineer at @Pando. Developer | Writer. From ABC to the world of code.