EJS (Embedded JavaScript) is a template engine that looks almost exactly like plain HTML, with small embedded JavaScript tags for dynamic content. This makes it one of the easiest engines to pick up if you already know HTML and JavaScript.
EJS Syntax Basics
EJS uses <%= %> to output an escaped value, <%- %> to output raw, unescaped HTML, and <% %> for control flow (loops, conditionals) that produces no output itself.
Because EJS templates are otherwise plain HTML, existing static markup can often be converted directly into a template just by adding a few embedded tags.
<!-- views/profile.ejs -->
<!DOCTYPE html>
<html>
<body>
<h1>Hello, <%= name %>!</h1>
<% if (isAdmin) { %>
<p>You have admin access.</p>
<% } %>
</body>
</html>
<%= name %> outputs the escaped value of the name variable; the <% if %> block controls whether the admin paragraph renders at all.
EJS Tag Reference
<%= value %> escaped output (safe for user data)
<%- html %> unescaped output (raw HTML)
<% code %> control flow, no output
<%# comment %> comment, not rendered
Always use <%= %> for any value that came from user input, to prevent XSS.
Only use <%- %> for HTML you generated and trust yourself, like a rendered partial.
Loops and conditionals use plain JavaScript syntax inside <% %> tags.
Install with npm install ejs, no separate configuration package is required.
EJS Cheatsheet
Common EJS patterns for lists, conditionals, and partials.
Task
Syntax
Escaped output
<%= user.name %>
Raw HTML output
<%- markdownHtml %>
Conditional
<% if (loggedIn) { %> ... <% } %>
Loop
<% items.forEach(item => { %> ... <% }) %>
Include a partial
<%- include('partials/header') %>
Looping Over Data
EJS control-flow tags accept any valid JavaScript, so rendering a list is just a forEach (or for/map) loop with an output tag inside it.
Using <%- %> for user-generated content, opening an XSS vulnerability.
Forgetting that <% %> produces no output at all, useful for control flow but not for displaying values.
Not setting app.set('views', ...) correctly, causing include() paths to resolve incorrectly.
Writing complex business logic directly inside a template instead of preparing the data in the route handler first.
Key Takeaways
EJS templates look like plain HTML with embedded JavaScript output and control-flow tags.
<%= %> escapes output (safe for user data); <%- %> outputs raw HTML (only for trusted content).
Loops and conditionals inside <% %> use ordinary JavaScript syntax.
include() composes shared partials like headers and footers across multiple pages.
Pro Tip
Default to <%= %> everywhere and only reach for <%- %> when you specifically need to render pre-built, trusted HTML (like an included partial), this habit alone prevents most templating-related XSS bugs.
You now know EJS syntax. Next, learn Pug's indentation-based syntax in the Pug lesson.