Template Engines

How Express Talks to a Template Engine

Express 24,430 does not render anything itself. It owns a lookup table from file extension to a callback; res.render finds the file, calls the callback, and sends whatever string comes back. Any package matching that signature is a view engine.

The engine contract, written by handJavaScript
app.engine('txt', (filePath, options, cb) => {
  fs.readFile(filePath, 'utf8')
    .then((s) => cb(null, s.replace(/\{\{(\w+)\}\}/g, (_, k) => options[k] ?? '')))
    .catch(cb);
});

Two settings drive the lookup: views, the directory to search (default ./views), and view engine, the extension assumed when res.render gets a name without one.

Configuring the view layerJavaScript
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('handlebars', engine({ defaultLayout: 'main' }));
app.get('/ejs', (req, res) => res.render('books', { title: 'Bookshelf', books }));
app.get('/hbs', (req, res) => res.render('books.handlebars', { title: 'Bookshelf', books }));

So res.render('books') looks for views/books.ejs, while a name carrying its own extension picks its own engine and one app can mix them. You rarely call app.engine for a popular engine: if nothing is registered for an extension, Express requires the package of that name and uses its __express export. The registry shows that lazy step.

Output of 28
after boot:        {"viewEngine":"ejs","engines":[".handlebars"]}
after 3 renders:   {"viewEngine":"ejs","engines":[".handlebars",".ejs",".pug"]}

The object you pass to res.render is merged over res.locals (per request) and app.locals (per process), so a site name set once at startup reaches every template. A lookup failure is not thrown; it goes to next(err) and reaches your error middleware (Error Middleware) as Failed to lookup view "missing" in views directory "...". Compiled templates are cached only when the view cache setting is on, which Express enables when NODE_ENV=production — forget that variable and every request recompiles every template.