Express.js β€’ Unit: Templating

Templating with EJS

Learn how Express.js can generate HTML dynamically using the Embedded JavaScript (EJS) template engineβ€”without filling your server code with long HTML strings.

Student-friendly notes ELI10 explanations Visual workflow Examples + code

1. Why do we need a template engine?

Think of a template as a form.
The HTML gives us the fixed design, while placeholders are empty spaces waiting for values such as a username. EJS fills those spaces before the final HTML reaches the browser.

❌ Without a template engine

HTML is written as JavaScript strings and values are joined using string concatenation.

app.get("/", (req, res) => {
  const username = "Ravi";

  res.send(
    "<html>" +
    "<body>" +
    "<h1>Welcome, " + username + "</h1>" +
    "</body>" +
    "</html>"
  );
});

βœ… With a template engine

Keep HTML in a separate template file and put placeholders where dynamic data should appear.

<h1>Welcome, <%= username %></h1>
Main idea: Express supplies the data, EJS combines the data with the template, and the browser receives the completed HTML.

Problems solved by templating

  • HTML is no longer mixed heavily with JavaScript.
  • String concatenation is easier to avoid.
  • Conditions, loops, and additional HTML can be handled inside the template.
  • Pages are easier to maintain because presentation is kept in template files.

2. Install and configure EJS

Install Express and EJS

Run this command in your project directory:

npm install express ejs

Tell Express to use EJS

const express = require("express");
const app = express();

app.set("view engine", "ejs");

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

Create the views folder

Express looks for template files in a directory named views by default. The views directory can also be configured explicitly using app.set("views", "...").

3. Create and render your first EJS template

Template: views/home.ejs

<!DOCTYPE html>
<html>
<head>
  <title>Home</title>
</head>
<body>
  <h1>Welcome, <%= username %></h1>
</body>
</html>

<%= username %> is the placeholder. Its value will come from the Express route.

Express route

app.get("/", (req, res) => {
  const username = "Ravi";

  res.render("home", {
    username: username
  });
});
Result: EJS replaces <%= username %> with Ravi, and the browser receives the completed HTML.

4. Visual learning: what happens internally?

🌐 Browser
requests /
β†’
⚑ Express Route
executes
β†’
🎨 res.render()
starts rendering
β†’
πŸ“„ home.ejs
template + data
β†’
βš™οΈ EJS
inserts values
β†’
πŸ“ƒ HTML
sent to browser
Easy memory trick:

Browser β†’ Express β†’ render β†’ EJS template + data β†’ final HTML β†’ Browser

5. Complete basic example

homeApp.js

const express = require("express");
const app = express();

app.set("view engine", "ejs");

app.get("/", (req, res) => {
  const username = "Ravi";

  res.render("home", {
    username: username
  });
});

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

6. EJS tags β€” your template toolbox

The source document introduces the following EJS tags and their purposes.

TagPurposeSimple idea
<%Scriptlet tag for control flow; produces no output.Run template logic
<%_Whitespace-slurping scriptlet; strips whitespace before it.Run logic + remove preceding whitespace
<%=Outputs a value into the template with HTML escaping.Show a value safely
<%-Outputs an unescaped value into the template.Output raw content
<%#Comment tag; no execution and no output.Template comment
<%%Outputs a literal <%.Show the tag itself
%>Plain ending tag.End the EJS block
-%>Trim-mode ending tag; trims the following newline.End + trim newline
_%>Whitespace-slurping ending tag; removes whitespace after it.End + remove following whitespace

7. Dynamic content: conditions, loops, and includes

Conditional rendering

If isAdmin is true, one message is displayed; otherwise another is displayed.

<% if (isAdmin) { %>
  <p>Welcome, Admin!</p>
<% } else { %>
  <p>Welcome, User!</p>
<% } %>

Looping through items

The template can use forEach() to create a list dynamically.

<ul>
  <% items.forEach(function(item) { %>
    <li>
      <%= item %>
    </li>
  <% }); %>
</ul>

Including a header

EJS can include another template file such as a header.

<%- include('header') %>
Think reusable: a shared header can be kept in its own file instead of copied into every page.

What Express passes

Express can pass values such as a name, admin status, and an array of items to the EJS template.

const data = {
  name: 'Geeks!',
  isAdmin: true,
  items: ['Apple', 'Banana', 'Orange',
          'Grapes', 'Mango']
};

res.render('index', data);

8. Project structure

Recommended layout from the example

project/
β”œβ”€β”€ index.js
└── views/
    β”œβ”€β”€ index.ejs
    β”œβ”€β”€ home.ejs
    β””── header.ejs

Explicitly setting the views directory

const path = require('path');

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

This makes the location of the template files explicit.

9. Full dynamic example

/index.js

// Import required modules
const express = require('express');
const path = require('path');

// Create an Express application
const app = express();

// Define the port
const port = 3000;

// Set EJS as the view engine
app.set('view engine', 'ejs');

// Set the views directory
app.set('views', path.join(__dirname, 'views'));

app.get('/', (req, res) => {
  // Sending this data from the server
  const data = {
    name: 'Geeks!',
    isAdmin: true,
    items: [
      'Apple',
      'Banana',
      'Orange',
      'Grapes',
      'Mango'
    ]
  };

  // Render the EJS template and pass the data
  res.render('index', data);
});

// Start the server
app.listen(port, () => {
  console.log(
    `Server is running at http://localhost:${port}`
  );
});

views/index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>EJS Example</title>
  <style>
    h1 { color: green; }
  </style>
</head>
<body>

  <!-- Include Header File -->
  <%- include('header') %>

  <!-- Conditional Example -->
  <% if (isAdmin) { %>
    <p>Welcome, Admin!</p>
  <% } else { %>
    <p>Welcome, User!</p>
  <% } %>

  <!-- Loop Example -->
  <ul>
    <% items.forEach(function(item) { %>
      <li>
        <%= item %>
      </li>
    <% }); %>
  </ul>

</body>
</html>

10. Quick revision mind map

EJS Templating
β†’
Template
HTML + EJS tags
+
Server Data
variables / arrays
β†’
Final HTML
Output:
<%= value %>
Logic:
<% ... %>
Reuse:
<%- include(...) %>

11. Key takeaways

🎯 Separate

Keep HTML presentation in EJS templates instead of constructing large HTML strings in routes.

πŸ”„ Dynamic

Server-side values can be inserted into the template before HTML is sent to the browser.

🧩 Reusable

Conditions, loops, and included templates make pages easier to organize and reuse.

12. Reference

The source document points students to these EJS references: