ejs

Embedded JavaScript templates for rendering HTML with plain JS control flow

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

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
77/100Good
Development Activity68
Maintenance60
Community80
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
78/100Good
Architecture75
Code Quality78
Innovation72
Learning Curve85

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,132

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
69
Dependency
Built with
TypeScript96%
Updated 3 days ago
JavaScript
99%
Other

ChartBrew

Analytics · Databases

4,041

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
65
Dependency
Built with
JavaScript99%
Updated 6 days ago
JavaScript
82%
Other

Countly

Analytics · Marketing

5,887

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
68
Dependency
Built with
JavaScript82%
Updated today
TypeScript
56%
MIT

Jitsu

Data Engineering

5,035

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

View details
89
Repo Health
79
Technical
68
Dependency
Built with
TypeScript56%
Go41%
Updated today
TypeScript
97%
Other

Kibana

Analytics · Monitoring

21,251

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
68
Dependency
Built with
TypeScript97%
Updated today
JavaScript
81%
MIT

localforge

AI Code Assistants · AI Agents · Developer Tools

396

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
72
Dependency
Built with
JavaScript81%
CSS12%
Updated 1 years ago
TypeScript
96%
Other

nango

Developer Tools · Automation · Authentication

11,501

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

View details
94
Repo Health
85
Technical
69
Dependency
Built with
TypeScript96%
Updated today
TypeScript
64%
Other

NocoDB

No Code Platforms · Databases · Low Code Platforms

64,586

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

View details
91
Repo Health
77
Technical
64
Dependency
Built with
TypeScript64%
Vue31%
Updated today
TypeScript
92%
Apache 2.0

OneUptime

Monitoring

7,481

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

View details
90
Repo Health
81
Technical
66
Dependency
Built with
TypeScript92%
Updated today

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

Search