NodeJSServer-Side Templating

Server-Side Templating

Not every app is a JSON API consumed by a separate frontend. Server-side templating generates complete HTML on the server — combining a template (the static markup) with data (from your database) — and sends finished pages to the browser. It's how traditional web apps, admin panels, emails, and server-rendered sites work. This page explains what a template engine is, how Express wires one up, and when server rendering beats a client-side SPA.

What a template engine does

Text
  Template (markup + placeholders)          Data (from your app)
  ┌─────────────────────────────┐          ┌──────────────────────┐
  │ <h1>Hello, <%= name %>!</h1>│    +     │ { name: 'Ada' }      │
  └─────────────────────────────┘          └──────────────────────┘
                       │  template engine renders
                       ▼
            <h1>Hello, Ada!</h1>   ← finished HTML sent to the browser
A template = static markup + dynamic placeholders
A template is HTML with holes in it. The engine fills those holes with your data — interpolating values (`<%= name %>`), looping over arrays, and branching on conditions — to produce a complete HTML string. The server sends that string; the browser just displays it. No client-side JavaScript framework is involved in producing the markup.
Wiring an engine into Express

JS
import express from 'express'
const app = express()

app.set('view engine', 'ejs')        // default engine for res.render
app.set('views', './views')          // where template files live

app.get('/', (req, res) => {
  // Render views/index.ejs with this data:
  res.render('index', { title: 'Home', user: req.user })
})
`res.render(view, data)` is the whole API
Configure the engine once with `app.set('view engine', ...)` and the views directory, then every handler calls `res.render('name', data)`. Express finds `views/name.<ext>`, runs it through the engine with `data`, and sends the resulting HTML — setting `Content-Type: text/html` for you. The `data` object's keys become variables available inside the template. You typically don't even `require` the engine; Express loads it by name.
The popular engines

Engine

Style

Looks like

Embedded JS in HTML

<%= value %> inside normal HTML

Indentation-based, terse

No angle brackets; whitespace-significant

Logic-less mustache

{{ value }}, minimal logic by design

Nunjucks

Jinja-like, powerful

{{ value }}, rich filters/inheritance

They differ in philosophy, not capability
All produce HTML from data — the difference is *style*. **EJS** keeps real JavaScript inside HTML (familiar, flexible). **Pug** trades HTML syntax for terse indentation (less typing, steeper learning). **Handlebars** deliberately limits logic in templates to keep them clean. Pick by team taste; the [next pages](/nodejs/ejs) cover each. EJS is the gentlest starting point because it's just HTML plus JS.
Layouts and partials — don't repeat markup

Text
views/
  layout.ejs          ← shared shell: <head>, nav, footer
  partials/
    header.ejs        ← reusable fragment
    footer.ejs
  index.ejs           ← page content, included into the layout
  users/
    list.ejs
    show.ejs
Partials keep the header/footer in one place
Every page shares a header, nav, and footer. Rather than copy that markup into each template, factor it into **partials** (included fragments) and a **layout** (a wrapper the page content slots into). Change the nav once, every page updates. Each engine has its own include/layout mechanism, but the goal is the same: DRY templates.
Auto-escaping — the security default

Text
<%= userInput %>   → escapes HTML:  &lt;script&gt;  (SAFE, default)
<%- userInput %>   → raw HTML, NOT escaped            (DANGEROUS)
Auto-escaping prevents XSS — never bypass it for user data
Template engines **escape** interpolated values by default, converting `<`, `>`, `&` into entities so user-supplied text can't inject `<script>` — this is your primary defense against [cross-site scripting](/nodejs/cors). Every engine also offers a "raw"/unescaped output (EJS `<%-`, Handlebars triple-stache `{{{ }}}`). Use raw output **only** for HTML you generated and fully trust — never for anything that originated from a user. Reaching for raw output to "make it render" is how XSS holes are born.
Server templating vs client-side SPA

Server templating

Client SPA + JSON API

HTML built

On the server

In the browser (JS)

First paint

Fast (HTML arrives ready)

Slower (load JS, then fetch)

SEO

Excellent (content in HTML)

Needs extra work (SSR/prerender)

Interactivity

Page reloads / sprinkles of JS

Rich, app-like

Best for

Content sites, admin, emails

Highly interactive apps

They're not mutually exclusive
Many apps mix both: server-rendered pages for content and SEO, with islands of client-side JS for interactivity — exactly what frameworks like Next.js (which renders React on the server) formalize. Templating isn't "old"; it's the right tool when you want HTML delivered ready-to-display. Choose based on how interactive the page must be, not fashion.
Next
Start with the most approachable engine — plain HTML plus JavaScript: [EJS](/nodejs/ejs).