Building Dynamic Views with NestJS
Bhavesh D.
Sep 8, 2025
Imagine this: You’re a backend developer working on a project where everything is about APIs. Day in, day out, you send JSON responses, test them in Postman, and hand them over to the frontend team. But then a thought hits you: What if I could actually render a full HTML page right from NestJS itself?
Most developers don’t know that NestJS (or even plain Express.js) can serve more than just JSON. In fact, with the help of a template engine like EJS, you can not only render HTML pages but also build fully functional admin panels, dashboards, or even server-side rendered apps all powered by NestJS.
In this blog, we’ll explore how to set up EJS with NestJS, use layouts and partials to keep things clean, and render HTML views directly from your controllers. By the end, you’ll have a working NestJS project that goes beyond JSON and serves dynamic HTML.
You can also find the complete source code in this GitHub repository: Nest EJS Demo (GitHub)
Why Use a Template Engine Instead of Raw HTML?
Sure, you could just serve static HTML from your backend, but template engines open up a whole new world of possibilities. With EJS, you get:
- Conditional rendering: Show or hide elements dynamically with if statements
- Loops: Render lists and data collections with ease
- Layouts: Define a base structure for pages and reuse it across views
- Partials: Create reusable snippets like headers, footers, and scripts
Setting Up EJS in NestJS
npm install ejs express-ejs-layouts
main.ts:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path/win32';
import { NestExpressApplication } from '@nestjs/platform-express';
const expressLayouts = require('express-ejs-layouts');
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// Set up view engine
app.useStaticAssets(join(__dirname, ' . . ', 'public')); // static files (css, js, images)
app.setBaseViewsDir(join(__dirname, ' . . ', 'views')); // views directory
app.setViewEngine('ejs');
// Enable layouts
app.set('layout', 'layouts/layout'); // default layout
app.use(expressLayouts);
await app.listen(process.env.PORT ?? 9000);
}
bootstrap();
- The public folder hosts static files like CSS, JS, and images
-
The views folder stores
.ejsfiles - express-ejs-layouts lets us define and use layouts across views
Folder Structure for Views
A typical structure looks like this:
views/ ├─ layouts/ │ └─ layout.ejs ├─ partials/ │ ├─ footer.ejs │ └─ header-css.ejs └─ example.ejs
-
The layouts folder holds base templates (e.g.,
layout.ejs, layout-auth.ejs) - The partials folder has reusable snippets (headers, footers, styles, scripts)
- The example.ejs file is rendered by the NestJS controller
Rendering EJS Views in NestJS Controllers
Let’s create a simple controller method to render an EJS view:
@Get('/example')
exampleRenderEjs(@Res() res) {
return res.render('example', {
title: 'Example',
main: 'This is an example body content',
FooterJs: ""
});
}
The example.ejs file will use the provided title and main data. You can pass any dynamic data from the backend into the template.
Using a Different Layout
You can also specify a different layout per route:
@Get('/example-layout-auth')
exampleLayoutAuthRenderEjs(@Res() res) {
return res.render('example', {
title: 'Example',
main: 'This is an example body content',
layout: 'layouts/layout-auth'
});
}
Here, instead of the default layout, we use layouts/layout-auth. This is particularly useful for scenarios like authentication pages where you need a different layout than your main application.
Working Example from GitHub
You can also find the complete source code in this GitHub repository: Nest EJS Demo (GitHub)
git clone https://github.com/bhaveshdaswani93/nest-ejs-demo.git
cd nest-ejs-demo
npm install
npm run start Conclusion
Serving HTML in NestJS isn’t just about returning static files. With EJS templates, you can build dynamic, reusable, and maintainable UI views directly from your backend. By combining layouts, partials, and route-based rendering, you unlock a powerful way to deliver HTML without relying solely on frontend frameworks.
Whether it’s a small admin dashboard, a quick reporting tool, or even server-rendered marketing pages, NestJS + EJS gives you the flexibility to go beyond JSON and build complete web experiences.
Full working example is available here: Nest EJS Demo (GitHub)