EJS Templating Engine
EJS (Embedded JavaScript) is a popular, fast, and lightweight templating engine for Node.js and browsers. It enables the rendering of dynamic HTML by embedding plain JavaScript directly within template files using special tags like `<% %>` for control flow, `<%= %>` for escaped output, and `<%- %>` for unescaped raw output. The current stable version is 5.0.2, with an active release cadence. Key differentiators include its straightforward approach, leveraging native JavaScript for logic, and broad compatibility across Node.js environments (CommonJS and ES Modules) and all modern browsers, down to very old ones. It offers features such as static caching of compiled functions and templates, custom delimiters, and full compliance with the Express view system, making it a common choice for server-side rendering in web applications due to its simplicity and effectiveness.
Common errors
-
ReferenceError: <variable_name> is not defined
cause This typically occurs when EJS is running in strict mode (`strict: true`) or with `_with: false`, and template variables are accessed directly instead of through the `locals` object.fixIn your template, change `<%= variable_name %>` to `<%= locals.variable_name %>`. Alternatively, ensure `strict: false` and `_with: true` are set in your EJS rendering options if direct variable access is desired. -
EJS: Could not find the include file "partial.ejs"
cause EJS cannot resolve the path for an included template. This usually happens when the `filename` option is missing (needed for relative paths), or the `root` or `views` options are not correctly configured to specify template directories.fixWhen using `ejs.compile` or `ejs.render` with includes, provide the `filename` option with the absolute path of the current template. For Express, configure `app.set('views', path.join(__dirname, 'views'))` and ensure include paths are correct relative to these view directories. -
TypeError: ejs.filters is not a function
cause You are attempting to use the `ejs.filters` global object, which was deprecated and completely removed in EJS v4.0.0.fixRemove any code referencing `ejs.filters`. Instead, pass custom helper functions as part of your template data object, or preprocess your data before passing it to EJS. -
Error: options.filename is required for caching
cause You are attempting to use the `cache: true` option with `ejs.compile` or `ejs.render` without providing the `filename` option. EJS uses the `filename` as the key for its internal cache.fixAlways provide the `filename` option with the absolute path to your template file when `cache: true` is enabled. For example, `ejs.render(str, data, { cache: true, filename: '/path/to/template.ejs' })`.
Warnings
- breaking The shortcut `ejs.render(dataAndOptions)` where all template data and rendering options are passed in a single object is officially deprecated and not recommended. It was effectively removed as a reliable pattern starting from v3.0.0 due to potential conflicts if an internal option name clashes with a data property name, leading to unpredictable behavior.
- breaking The `ejs.filters` object, which provided a global mechanism for defining custom filter functions, was removed in EJS v4.0.0. Any code relying on `ejs.filters` will no longer function.
- breaking The `ejs.clearCache()` method, which allowed programmatic clearing of EJS's internal template cache, was removed in EJS v5.0.0. There is no direct public API to clear the cache anymore.
- gotcha EJS templates are essentially JavaScript runtimes. Directly passing unchecked user input (e.g., from `req.query`, `req.body`, or `req.params`) as the template string or influencing sensitive options like `filename`, `root`, or `closeDelimiter` can lead to severe Server-Side Template Injection (SSTI) and Remote Code Execution (RCE) vulnerabilities. The EJS project explicitly warns against this, stating that the `render` function is not intended for untrusted input.
- gotcha By default, EJS uses JavaScript's `with()` statement (`_with: true`) to expose local variables directly in the template scope. However, when `strict: true` is enabled or `_with: false` is explicitly set, `with()` is not used. In these scenarios, template variables must be accessed via a `locals` object (e.g., `<%= locals.user.name %>` instead of `<%= user.name %>`), otherwise, a `ReferenceError` will occur.
Install
-
npm install ejs -
yarn add ejs -
pnpm add ejs
Imports
- ejs
const ejs = require('ejs');import ejs from 'ejs';
- ejs.render
import { render } from 'ejs'; const html = render(templateString, data, options);import ejs from 'ejs'; const html = ejs.render(templateString, data, options);
- ejs.renderFile
import { renderFile } from 'ejs';import ejs from 'ejs'; ejas.renderFile(filename, data, options, callback);
Quickstart
import ejs from 'ejs';
import express from 'express';
import path from 'path';
const app = express();
const port = process.env.PORT ?? 3000;
// Configure EJS as the view engine for Express
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Create a simple EJS template file (e.g., views/index.ejs):
// <!-- views/index.ejs -->
// <!DOCTYPE html>
// <html lang="en">
// <head>
// <meta charset="UTF-8">
// <meta name="viewport" content="width=device-width, initial-scale=1.0">
// <title>EJS Example</title>
// </head>
// <body>
// <h1>Hello, <%= user.name %>!</h1>
// <% if (isAdmin) { %>
// <p>You are an administrator.</p>
// <% } else { %>
// <p>Welcome, regular user.</p>
// <% } %>
// <p>Current time: <%= currentTime.toLocaleTimeString() %></p>
// </body>
// </html>
// Route to render a template file
app.get('/', (req, res) => {
const userData = {
user: { name: 'Alice' },
isAdmin: true,
currentTime: new Date()
};
res.render('index', userData);
});
// Route to render a template string directly
app.get('/direct', (req, res) => {
const templateString = `<h2>Direct Render</h2><p>Server says: <%= message %></p>`;
const data = { message: 'Hello from direct EJS render!' };
const html = ejs.render(templateString, data);
res.send(html);
});
app.listen(port, () => {
console.log(`EJS example app listening at http://localhost:${port}`);
console.log(`Try http://localhost:${port}/ and http://localhost:${port}/direct`);
});