ejs

Embedded JavaScript templates for rendering HTML with plain JS control flow

Library
npm
v6.0.1
8,130 stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
62 /100 Good
Development Activity 32
Maintenance 36
Community 80
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
78 /100 Good
Architecture 75
Code Quality 78
Innovation 72
Learning Curve 85

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 the Template class — 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 of views roots, or a custom includer callback, with per-include caching via options.cache
  • Drop-in Express view-engine compatibility through ejs.__express (aliased to renderFile), so app.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') and import 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 (unsafePrototypeLocals opt-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.__express as the configured view engine for .ejs templates
  • 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.js and 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

TypeScript
96%
Other

CapRover

Developer Tools · Devops · Hosting Control Panel

15,173

Deploy any app, database, or website to your own server in minutes—no Docker or Linux expertise required.

View details
87
Repo Health
82
Technical
67
Dependency
Built with
TypeScript 96%
Updated 1 weeks ago
JavaScript
99%
Other

ChartBrew

Analytics · Databases

4,063

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.

View details
90
Repo Health
83
Technical
64
Dependency
Built with
JavaScript 99%
Updated 1 weeks ago
JavaScript
83%
Other

Countly

Analytics · Marketing

5,908

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.

View details
96
Repo Health
82
Technical
65
Dependency
Built with
JavaScript 83%
Updated 4 days ago
TypeScript
62%
MIT

Jitsu

Data Engineering

5,094

Open-source, fully-scriptable data ingestion engine that streams events from web, apps, and APIs to any data warehouse in real time.

View details
88
Repo Health
79
Technical
66
Dependency
Built with
TypeScript 62%
Go 36%
Updated 1 weeks ago
TypeScript
98%
Other

Kibana

Analytics · Monitoring

21,301

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.

View details
98
Repo Health
87
Technical
63
Dependency
Built with
TypeScript 98%
Updated 4 days ago
JavaScript
81%
MIT

localforge

AI Agents · AI Code Assistants · Developer Tools

399

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.

View details
46
Repo Health
54
Technical
73
Dependency
Built with
JavaScript 81%
CSS 12%
Updated 1 years ago
TypeScript
97%
Other

nango

Authentication · Automation · Developer Tools

12,376

Build product integrations with AI using 800+ APIs — auth, proxy, and TypeScript functions on production-grade infrastructure.

View details
93
Repo Health
85
Technical
67
Dependency
Built with
TypeScript 97%
Updated 1 weeks ago
TypeScript
65%
Other

NocoDB

Databases · Low Code Platforms · No Code Platforms

65,098

Turn any SQL database into a collaborative no-code spreadsheet with automatic REST APIs and real-time views.

View details
90
Repo Health
77
Technical
62
Dependency
Built with
TypeScript 65%
Vue 30%
Updated 6 days ago
TypeScript
95%
Apache 2.0

OneUptime

Monitoring

7,668

The complete open-source observability platform that replaces PagerDuty, Datadog, Sentry, and StatusPage with a single self-hostable system.

View details
91
Repo Health
81
Technical
65
Dependency
Built with
TypeScript 95%
Updated 4 days ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers