-
-
Notifications
You must be signed in to change notification settings - Fork 10
EJS Syntax Reference
Source for this Syntax Document - https://github.com/mde/ejs/blob/main/docs/syntax.md
EJS is designed to be flexible and easy-to-write, but without too much abstractions to cover up the HTML base.
- Basic format
- Delimiters
- Starting tags
-
<%=
: Escaped output -
<%-
: Unescaped output -
<%#
: Comments -
<%
: Scriptlet -
<%_
: Scriptlet, removes all preceding whitespace
-
- Ending tags
-
%>
: Regular ending tag -
-%>
: Removes trailing newline -
_%>
: Removes all trailing whitespace
-
- Literal tags
- Including other files
- “Preprocessor” directive
- JavaScript
include()
function
- Copyright
An EJS “tag” is the primary functioning unit in an EJS template. With the exception of the literal tags, all tags are formed by the following format:
<starting content closing>
The spaces between starting and content, and content and closing are not required; they are recommended though for readability.
The starting and closing tags contain a special string called the
delimiter. In this document, all tags are shown using the %
delimiter, which
is the default. You can, however, change that to your liking. See
https://github.com/mde/ejs#custom-delimiters for more information on how to
change it.
The most important thing for a template language is the ability to pass
variables to the template. In EJS, this is done with the <%=
and <%-
tags.
<%=
is the starting tag to use for variables that need to be escaped. If the
specified string contains forbidden characters like <
and &
, they are
escaped automatically with HTML codes.
The content of the tag can be any valid JavaScript operators, so tags like
<%= name ? name : (lastName || 'John Doe') %>
would work as intended.
<p>Hello, <%= name %>.</p>
<p>Hello, <%= 'the Most Honorable ' + name %>.</p>
{
"name": "Timoth<y>"
}
<p>Hello, Timoth<y>.</p>
<p>Hello, the Most Honorable Timoth<y>.</p>
If your local contains preformatted HTML, you might not want to escape it. In
this case, use the <%-
tag.
However, always be 100% sure the rendered local is sanitized, to prevent cross-site scripting (XSS) attacks.
<p>Hello, <%- myHtml %>.</p>
<p>Hello, <%= myHtml %>.</p>
<p>Hello, <%- myMaliciousHtml %>.</p>
<p>Hello, <%= myMaliciousHtml %>.</p>
{
"myHtml": "<strong>Timothy</strong>"
, "myMaliciousHtml": "</p><script>document.write()</script><p>"
}
<p>Hello, <strong>Timothy</strong>.</p>
<p>Hello, <strong>Timothy</strong>.</p>
<p>Hello, </p><script>document.write()</script><p>.</p>
<p>Hello, </p><script>document.write()</script><p>.</p>
The <%#
starting tag denotes that the statement is a comment that is not to
be executed or rendered in the resulting HTML.
The use of <%#
might cause some useless whitespace, as illustrated by the
example below. You can trim it using the -%>
ending tag.
<div>
<%# comment %>
</div>
<div>
<%# comment -%>
</div>
<div>
</div>
<div>
</div>
Scriptlets in the <%
tag allows logic to be embedded in an EJS template. You
are free to use any JavaScript syntax in this tag, and to mix JavaScript
with EJS. You can also put multiple statements in one tag.
All types of JavaScript comments are allowed, although it is preferable to use
the <%#
tag for comments. For example, the following three code blocks are
equivalent, though <%#
is the shortest.
<%# comment %>
<%/* comment */%>
<%// comment %>
Always use brackets in loops and conditionals that involves mixing EJS template and JavaScript scriptlets. Omitting brackets might work for some statements, but the behavior is undefined and subject to change.
It is not necessary to use curly brackets for scriptlet-only code.
<%# Bad practice %>
<% if (true) %>
<p>Yay it's true!</p>
<%# Good practice %>
<% if (true) { %>
<p>Yay it's true!</p>
<% } %>
<%# These are all valid statements %>
<% var output
, exclamation = ''
, shouldOutput = false
if (true)
output = 'true!'
if (true) {
exclamation = 'Yay! ';
}
if (true) shouldOutput = true; %>
<% if (shouldOutput) { %>
<%= exclamation + 'It\'s ' + output %>
<% } %>
Line breaks are allowed in <%
tags.
Unless the statement involves mixing EJS and JavaScript scriptlet, always put complete statements in a tag. For example, the following works:
<% var stringToShow = thisIsABooleanVariableWithAVeryLongName
? 'OK'
: 'not OK' %>
While the following does not:
<% var stringToShow = thisIsABooleanVariableWithAVeryLongName %>
<% ? 'OK' %>
<% : 'not OK' %>
As is in JavaScript, semicolons are not required if proper line breaks are preserved.
The use of scriptlets might cause some useless whitespace, as illustrated by the example below. You can trim it by
- using the
-%>
ending tag, and - using the
<%_
starting tag or starting the tag in the beginning of a line.
In the following example, several different coding styles are used simultaneously, to show that EJS is flexible with regards to personal habits. It does not mean that we recommend mixing coding styles in your own project.
<dl>
<%for (var i = 0; i < users.length; i++) { %><%
var user = users[i]
, name = user.name // the name of the user
%><%# comment %>
<%var age = user.age; /* the age of the user */%>
<dt><%= name %></dt>
<dd><%= age %></dd>
<%}-%>
</dl>
{
"users": [
{
"name": "Timothy"
, "age": 15
}
, {
"name": "Juan"
, "age": 51
}
]
}
<dl>
<dt>Timothy</dt>
<dd>15</dd>
<dt>Juan</dt>
<dd>51</dd>
</dl>
This tag is the same as a Scriptlet, except that it removes all whitespace before it.
<ul>
<% users.forEach(function(user, i, arr){ -%>
<li><%= user %></li>
<% }); -%>
</ul>
<ul>
<%_ users.forEach(function(user, i, arr){ -%>
<li><%= user %></li>
<%_ }); -%>
</ul>
<ul>
<li>Anne</li>
<li>Bob</li>
</ul>
<ul>
<li>Anne</li>
<li>Bob</li>
</ul>
There are three flavors of ending tags: the regular one, the newline-trimming one, and the whitespace-slurping one.
As used in all of the examples above, %>
is the standard tag used to end an
EJS expression.
-%>
trims all extra newlines a scriptlet or a comment might cause. It does
not have any effect on output tags.
Beginning of template
<% 'this is a statement'
+ ' that is long'
+ ' and long'
+ ' and long' %>
End of template
---
Beginning of template
<% 'this is a statement'
+ ' that is long'
+ ' and long'
+ ' and long' -%>
End of template
Beginning of template
End of template
---
Beginning of template
End of template
_%>
removes all whitespace after it.
To output literal <%
or %>
, use <%%
or %%>
, respectively. If a customized delimiter is used, use
the same syntax. E.g. use <$$
to get <$
if the delimiter is $
.
In regards to all the other tags, the literal tags are special as they do not need a closing tag to function.
However, think twice before you use these tags because <
and >
characters might
need to be escaped as <
and >
, respectively.
The following example wrap <%
and %>
in a <pre>
, where it is not necessary to
escape <
or >
at all.
<pre>This is literal: <%%</pre>
<pre>This is literal too: <%% %></pre>
<pre>This is literal as well: %%></pre>
<pre>This is literal: <%</pre>
<pre>This is literal too: <% %></pre>
<pre>This is literal as well: %></pre>
EJS offer two ways of including other files. You can even include files that are not EJS templates, as the case is for CSS stylesheets.
For both flavors, if the file specified does not have an extension, .ejs
is
automatically appended. If it is an absolute path, that file is included.
Otherwise, the file is assumed to be in the same directory as the parent
template.
The behavior of resolving included file path can be overridden using the
ejs.resolveInclude
function.
As a compatibility layer with EJS version 1, it is possible to use the
include
directive in an unescaped output tag to directly “yank” the
text from another file, just like including a C header. However, as it is done
as a simple inclusion, you cannot pass arguments to the included template. You
can however make variables available in the parent template, that will be
visible to the child template as well.
This flavor of include
is static, which means that the resulting
function contains the copy of the included file as when it was compiled, so
if you changed the file after compilation, the changes are not reflected.
You most probably should not use the -%>
ending tag on an include
directive, as it trims the whitespace after the included file.
<li><%= pet.name %></li>
<ul>
<% pets.forEach(function (pet) { -%>
<% include included %>
<% }) -%>
</ul>
{
"pets": [
{
"name": "Hazel"
}
, {
"name": "Crystal"
}
, {
"name": "Catcher"
}
]
}
<ul>
<% pets.forEach(function (pet) { -%>
<li><%= pet.name %></li>
<% }) -%>
</ul>
<ul>
<li>Hazel</li>
<li>Crystal</li>
<li>Catcher</li>
</ul>
With the release of EJS version 2, we have added a new way of including files
that is more intuitive. The include()
function is available to the
templates, with the following signature:
include(filename, [locals])
One major difference with the method described above is that the variables in
the parent function are not visible to the child template, unless it is
explicitly declared in the locals
object, or is passed as a local to the
parent template.
Also, the included file is compiled upon execution of the script, which means performance might be theoretically lower than the “preprocessor” flavor. In practice however, caching can make this difference negligible.
Some cautions MUST to be taken if the included filename is fetched from a
user during rendering time, as one could easily use private files as the file
name, like /etc/passwd
or ../api-keys
.
This has the exact same effect as the example for the include
directive.
<li><%= pet.name %></li>
<ul>
<% pets.forEach(function (pet) { -%>
<%- include('included', {
pet: pet
}) %>
<% }) -%>
</ul>
{
"pets": [
{
"name": "Hazel"
}
, {
"name": "Crystal"
}
, {
"name": "Catcher"
}
]
}
<ul>
<li>Hazel</li>
<li>Crystal</li>
<li>Catcher</li>
</ul>