ejs
Embedded JavaScript templates for rendering HTML with plain JS control flow
Repository Health
Technical Analysis
ejs (Embedded JavaScript templates) is a templating engine that lets you generate HTML markup using plain JavaScript inside familiar <% %> tags rather than a bespoke templating syntax. It compiles templates into JavaScript functions ahead of render time, supports escaped and raw output, includes, custom delimiters, and both synchronous and async rendering, and runs identically on the server (Node.js, CommonJS or ESM) and in the browser.
Because its control-flow tags execute real JavaScript, there’s no separate mini-language to learn for loops, conditionals, or expressions — if you know JS, you already know ejs. It ships a CLI for one-off rendering from the command line, complies with the Express view-engine contract via ejs.__express, and as of v6 removes the CJS/ESM interop shim that previously broke under ESM-aware bundlers like Vite, esbuild, and Bun.
What You Get
- Four core rendering entry points —
ejs.compile(),ejs.render(),ejs.renderFile(), and theTemplateclass — covering pre-compilation, one-shot rendering, and file-based rendering with a Node-style(err, str)callback - Full tag set:
<%(scriptlet),<%=(escaped output),<%-(raw output),<%#(comment),<%_/_%>(whitespace slurping), and-%>(newline trim), plus configurable custom delimiters - Runtime
include()support that resolves relative or absolute paths, an array ofviewsroots, or a customincludercallback, with per-include caching viaoptions.cache - Drop-in Express view-engine compatibility through
ejs.__express(aliased torenderFile), soapp.set('view engine', 'ejs')works without extra glue code - A bundled CLI (
bin/cli.js) for rendering templates from files or piped stdin with JSON data input, output-file redirection, and custom delimiter flags - Dual CJS/ESM publish targets (
require('ejs')andimport ejs from 'ejs') plus a prebuilt browser/UMD bundle (ejs.min.js) for client-side rendering with no bundler required - A v6 prototype-pollution mitigation (
unsafePrototypeLocalsopt-in) that changed how locals resolve identifiers through the prototype chain by default
Common Use Cases
- Server-rendered HTML views in Express apps, using
ejs.__expressas the configured view engine for.ejstemplates - Generating one-off HTML fragments or emails from a data object via
ejs.render(str, data)without touching the filesystem - Static site or build-time HTML generation, precompiling templates with
ejs.compile()and caching the resulting function for repeated renders - Client-side templating in the browser by loading
ejs.min.jsand rendering markup directly from JSON data without a server round-trip - Composing multi-file page layouts (headers/footers/partials) with
<%- include('partial', {data}) %>resolved relative to the including template
Under The Hood
Architecture: ejs is a single-pass template compiler. Template (lib/esm/ejs.js) parses the source string against _REGEX_STRING — the regex that recognizes all tag delimiters — splitting it into literal text and JS code segments, then assembles those segments into the body of a new Function(...) that, when called with a locals object, returns the rendered string. ejs.compile() wraps this in the public API and returns the compiled function directly; ejs.render() and ejs.renderFile() layer caching (handleCache/tryHandleCache) and Express-specific options normalization (pulling views/cache out of data.settings for Express 2-4 compatibility) on top of the same Template machinery. Includes are resolved at template-compile time via getIncludePath/resolvePaths, walking the configured root/views array or an includer callback, and are themselves compiled and optionally cached as nested Template instances.
Tech Stack: Pure JavaScript with zero runtime dependencies — package.json lists only devDependencies (jake as the build/test runner, mocha for tests, eslint, typescript for type declarations, uglify-js/browserify for the client bundle). The published package exposes dual entry points (lib/cjs/ejs.js for require, lib/esm/ejs.js for import) generated from a single ESM source via a jakefile-driven CJS compile step, plus a browser UMD build (ejs.min.js) referenced by the browser/jsdelivr/unpkg package.json fields. engines targets Node >=0.12.18, reflecting the project’s long-running commitment to backward compatibility.
Code Quality: The test suite (test/ejs.js, 1223 lines, TDD-style suite/test blocks run via Mocha with --check-leaks) covers roughly 125 cases spanning tag parsing, includes, caching, async rendering, Express option-passing quirks, and error-stack rewriting (rethrow). Public functions carry JSDoc annotations (@param, @return, @public/@private) throughout lib/esm/ejs.js, and error handling favors explicit, descriptive throws (e.g. rethrow rewrites the generated function’s stack trace back to template line numbers) over silent failures. A dedicated SECURITY.md documents the trust boundary explicitly: ejs executes arbitrary JavaScript by design, so passing unsanitized user input as template source (not data) is documented as inherently unsafe rather than treated as a bug.
API Design: The four-function surface (compile, render, renderFile, Template) maps directly onto the three ways templates get used (precompiled reuse, one-shot string render, file-based render), keeping the common case — ejs.render(str, data) — a single call with sensible defaults. The options object is consistently named and documented (filename, cache, rmWhitespace, strict, async, etc.) across all three functions rather than diverging per entry point, and the Express-compatibility shimming in renderFile is isolated so it doesn’t leak into the plain render path. The tradeoff is that some behavior (like with()-based local-variable scoping vs. strict/destructuredLocals) requires reading the options table to understand, since JS’s own scoping rules don’t map 1:1 onto template locals.
Used by 14 apps in this directory
CapRover
Developer Tools · Devops · Hosting Control Panel
Deploy any app, database, or website to your own server in minutes—no Docker or Linux expertise required.
ChartBrew
Analytics · Databases
Open-source reporting platform to build live dashboards from SQL, NoSQL, APIs, and SaaS tools with an AI assistant that creates charts from natural language.
Countly
Analytics · Marketing
Privacy-first, self-hosted analytics and customer engagement platform with full data ownership, GDPR compliance, and AI-powered insights across mobile, web, desktop, and IoT.
Jitsu
Data Engineering
Open-source, fully-scriptable data ingestion engine that streams events from web, apps, and APIs to any data warehouse in real time.
Kibana
Analytics · Monitoring
Your open source window into the Elastic Stack — query, visualize, and act on data stored in Elasticsearch with real-time dashboards, AI-assisted search, and automated alerting.
localforge
AI Code Assistants · AI Agents · Developer Tools
A local, multi-provider AI coding agent with a real desktop and web UI — bring your own Anthropic, OpenAI, Gemini, Bedrock, or local Ollama keys and let it read, edit, and run code in your own project folder.
nango
Developer Tools · Automation · Authentication
Build product integrations with AI using 800+ APIs — auth, proxy, and TypeScript functions on production-grade infrastructure.
NocoDB
No Code Platforms · Databases · Low Code Platforms
Turn any SQL database into a collaborative no-code spreadsheet with automatic REST APIs and real-time views.
OneUptime
Monitoring
The complete open-source observability platform that replaces PagerDuty, Datadog, Sentry, and StatusPage with a single self-hostable system.