Template Engines, Partials, and Layouts
In this assignment, you will transform your static HTML website into a dynamic, maintainable application using the EJS templating engine. You'll learn why templating engines are essential for modern web development, how to create reusable components called partials, and how to organize your views for better maintainability.
By the end of this assignment, you'll understand how templating engines solve real problems that static HTML cannot address, and you'll have the foundation for building scalable web applications.
The Problem with Static HTML
Your current Express application works, but it has several significant limitations that become apparent as websites grow larger. Consider what happens when you want to make a simple change, like updating your navigation menu or changing the copyright year in your footer.
With static HTML files, you would need to open every single HTML file and make the same change in each one. Miss one file, and your site becomes inconsistent. Add ten more pages, and you have ten more places where changes must be made perfectly. This approach doesn't scale and leads to maintenance nightmares.
Static HTML also cannot adapt to different situations. You cannot change content based on who's visiting, what time it is, or data from a database. Every visitor sees exactly the same thing, which severely limits what your application can do.
Templating engines solve these problems by separating your content from your layout, allowing you to create reusable components, and enabling dynamic content generation. This assignment will show you how.
Understanding Templating Engines
A templating engine is a tool that combines templates (HTML with special syntax) and data to produce final HTML that gets sent to the browser. Think of it like a mail merge in a word processor: you have a template with placeholders, and the engine fills in those placeholders with actual data.
EJS (Embedded JavaScript) is one of many templating engines available for Express. It allows you to embed JavaScript directly in your HTML using special tags, making it easy to create dynamic content while keeping your templates readable.
Here's how the process works: when someone visits your website, Express finds the appropriate EJS template, combines it with any data you provide, processes the special EJS syntax, and sends the resulting HTML to the user's browser. The user never sees the EJS code – only the final HTML.
Preparation
We'll be building on your Express application from the previous assignment. Before we begin, let's make one small addition to improve your development experience.
Add Development Environment Configuration
Add NODE_ENV=development to your .env file (on a new line after your PORT setting). This tells your application that it's running in development mode, which will be useful for features like debugging and live reloading in future assignments:
NODE_ENV=development
PORT=3000
Then, in your server.js file, add this variable alongside your existing PORT variable:
const NODE_ENV = process.env.NODE_ENV || 'production';
const PORT = process.env.PORT || 3000;
The default of production ensures your application runs securely if the environment variable isn't set, which is exactly what you want when deploying to a live server.
Assignment Instructions
1. Install and Configure EJS
First, install the EJS templating engine using pnpm:
pnpm add ejs
Next, configure Express to use EJS by adding these lines to your server.js file. Place them in your middleware configuration section just after your public static directory configuration:
// Set EJS as the templating engine
app.set('view engine', 'ejs');
// Tell Express where to find your templates
app.set('views', path.join(__dirname, 'src/views'));
The first line tells Express to use EJS for processing templates. The second line specifies where your template files are located. Express will automatically look for .ejs files in this directory when you render views.
These configuration calls must come before your routes because Express needs to know about the templating engine before it tries to render any templates. Think of it as setting up the kitchen before you start cooking.
2. Understanding Partials
Before we start creating templates, let's understand the concept of partials. A partial is a reusable template fragment that contains code you want to use in multiple places. Think of partials as the template equivalent of functions in programming – they let you write something once and use it everywhere.
For example, instead of copying your navigation menu into every page template, you create one navigation partial and include it wherever you need it. Change the navigation once, and it updates everywhere automatically.
Create a partials directory inside your src/views directory. This is where we'll store our reusable template components.
3. Create Header and Footer Partials
Now let's create our first partials. These will contain the common elements that appear on every page.
Create the Header Partial
Create src/views/partials/header.ejs with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/products">Products</a></li>
</ul>
</nav>
</header>
Notice the <%= title %> syntax. This is EJS telling the template engine to insert the value of a variable called title. The <%= %> tags mean "output this value into the HTML."
Create the Footer Partial
Create src/views/partials/footer.ejs with this content:
<footer>
<p>© 2025 My Express App.</p>
</footer>
</body>
</html>
Together, these partials provide the complete HTML structure that every page needs, but they leave room in the middle for page-specific content.
4. Convert Static HTML to EJS Templates
Now comes the transformation. We'll convert your static HTML files into dynamic EJS templates that use the partials we just created.
Rename Your HTML Files
First, rename your existing HTML files to use the .ejs extension:
home.html→home.ejsabout.html→about.ejsproducts.html→products.ejs
Replace Content with Template Structure
Now edit each .ejs file to use this simple structure:
<%- include('partials/header') %>
<main>
<h1><%= title %></h1>
<!-- Put your page-specific content here -->
</main>
<%- include('partials/footer') %>
The <%- include() %> syntax tells EJS to insert the contents of another template file. The %- means "include this content without escaping it" (since we want to include raw HTML).
For example, your home.ejs might look like this:
<%- include('partials/header') %>
<main>
<h1><%= title %></h1>
<p>Welcome to my Express application! This site demonstrates the power of templating engines.</p>
<img src="https://picsum.photos/600/400" alt="Random image">
</main>
<%- include('partials/footer') %>
When converting your files, remove all the HTML structure elements like <!DOCTYPE html>, <html>, <head>, <body>, etc. These are now handled by your partials. Include only the unique content for each page within the <main> tags.
5. Update Your Routes to Use Templates
Now we need to update your routes to render EJS templates instead of sending static HTML files. The key change is switching from res.sendFile() to res.render().
Update your routes in server.js to look like this:
/**
* Routes
*/
app.get('/', (req, res) => {
const title = 'Welcome Home';
res.render('home', { title });
});
app.get('/about', (req, res) => {
const title = 'About Me';
res.render('about', { title });
});
app.get('/products', (req, res) => {
const title = 'Our Products';
res.render('products', { title });
});
Here's what changed:
-
res.render()instead ofres.sendFile(): This tells Express to process an EJS template rather than send a static file -
Template name without extension:
'home'automatically looks forhome.ejsin your views directory -
Data object: The second parameter
{ title }provides data that the template can use
When you write res.render('home', { title }), you're passing an object with a title property to the template. Inside the EJS template, <%= title %> outputs the value of that property. The property names in your data object must match the variable names used in your templates.
6. Test Your Templated Application
Your project structure should now look like this:
[project-root]
├── public/
│ └── css/
│ └── main.css
├── src/
│ └── views/
│ ├── partials/
│ │ ├── header.ejs
│ │ └── footer.ejs
│ ├── about.ejs
│ ├── home.ejs
│ └── products.ejs
├── .env
└── server.js
Start your server with pnpm run dev and test each page:
- Verify that the page titles appear correctly in the browser tab
- Check that the navigation works on all pages
- Confirm that your CSS styles are still applied
- Make sure the footer appears on every page
If everything works correctly, you've successfully converted from static HTML to dynamic templates! Now try changing something in your header partial and refresh the page – you'll see the change reflected on all pages immediately.
Understanding EJS Syntax
Now that you have a working templated application, let's review some the EJS syntax you've been using:
-
<%= variable %>: Outputs the value of a variable into the HTML (escapes HTML characters for security) -
<%- variable %>: Outputs the value without escaping (use for including HTML content) -
<% code %>: Runs JavaScript code without outputting anything (useful for loops and conditionals) -
<%- include('file') %>: Includes another template file
These simple tags give you powerful capabilities for creating dynamic content while keeping your templates readable and maintainable.
Key Concepts Summary
By completing this assignment, you've solved the problems we identified with static HTML:
- Maintainability: Change your navigation or footer once, and it updates everywhere
- Consistency: All pages share the same structure and styling automatically
- Dynamic Content: Page titles change based on the route, and you can easily add more dynamic elements
- Scalability: Adding new pages requires only creating the unique content, not duplicating the entire structure
- Organization: Related code is grouped together, making your project easier to understand and modify
These benefits will become even more apparent as you add more features and pages to your application.
Check Your Understanding
A student just completed their first assignment learning EJS templating with Express.js. They learned how to: - Use res.render() instead of res.sendFile() to render EJS templates - Pass data to templates using objects like { title: 'Page Title' } - Create and use partials (header.ejs and footer.ejs) - Use <%- include('partials/header') %> to include partials - Use <%= variable %> to output variables in templates - Convert from static HTML to dynamic EJS templates\n They're now working on a coding challenge to create a student information page. The challenge asks them to: 1. Complete a route handler that renders the 'student' template with student data (name, id, email, address) 2. Complete the student.ejs template that includes header/footer partials and displays the student data\n When reviewing their code, focus specifically on: - Did they use res.render() correctly with the right template name? - Did they pass a proper data object with all required student properties? - Did they include the header and footer partials correctly using <%- include() %>? - Did they use <%= %> syntax correctly to output the student data? - Are their variable names consistent between the route and template?\n Common mistakes to watch for: - Using res.sendFile() instead of res.render() - Forgetting to pass data or passing it incorrectly - Missing quotes around partial names in include statements - Using wrong EJS syntax (<% instead of <%= for output) - Mismatched variable names between route and template - Forgetting to include partials or including them incorrectly\n Give encouraging, specific feedback. If they make mistakes, guide them to the specific concept they need to review rather than giving them the answer. Only provide hints that help them think through the problem using what they just learned in this assignment.\n
/**
* Using the skills you learned in this assignment, complete the following tasks:
* 1. Complete the route handler for the '/student' route ensuring to render the 'student' template
* with the appropriate data object containing a student's name, id, email, and address
* 2. Complete the student.ejs template ensuring it includes the header and footer partials
* and displays the student's information (name, id, email, address) correctly
*/
// File: server.js (Route Handler)
app.get('/student', (req, res) => {
...
res.render('...', { ... });
});
// File: /src/views/student.ejs (View Template)
<%- ... %>
<main>
<div class="student-info">
<h1>Student Information</h1>
<p>Name: ...</p>
<p>Id #: ...</p>
<p>Email: ...</p>
<p>Address: ...</p>
</div>
</main>
<%- ... %>
Explore Further
To deepen your understanding of templating concepts, try these optional enhancements:
-
Dynamic Copyright Year: Modify your footer partial to display the current year using
<%= new Date().getFullYear() %> -
Head Partial: Extract the
<head>section into its own partial for even better organization - Navigation Partial: Create a separate navigation partial that can be reused across different layouts and remove the hardcoded links from your header partial
These exercises will help you understand how templating engines enable more sophisticated dynamic content generation.