Checking with typeof
Before accessing properties, check the type of your data. This prevents errors when data might not be what you expect.
<% if (typeof user !== 'undefined' && user) { %>
<p>Hello, <%= user.name %></p>
<% } else { %>
<p>Please log in.</p>
<% } %>
The typeof check ensures the variable exists before you try to use it.
Using && Guards
The && operator provides a shorter way to guard against undefined values. It only proceeds if the first part is truthy.
<p>Name: <%= user && user.name || 'Guest' %></p>
<p>Email: <%= user && user.email || 'No email' %></p>
This pattern is clean and readable for simple fallback values.
<% if (user && user.posts && user.posts.length) { %>
<h3>Your Posts</h3>
<% user.posts.forEach(post => { %>
<article><%= post.title %></article>
<% }); %>
<% } %>
Optional Chaining Pattern
While EJS doesn't support the ?. operator directly, you can achieve similar safety by chaining checks or using a helper function.
<% const city = user && user.address && user.address.city; %>
<p>City: <%= city || 'Unknown' %></p>
For cleaner templates, create a helper function in your middleware.
app.locals.get = (obj, path, defaultVal) => {
const keys = path.split('.');
let result = obj;
for (const key of keys) {
result = result && result[key];
if (result === undefined) return defaultVal;
}
return result || defaultVal;
});
Now you can safely access nested properties.
<p>City: <%= get(user, 'address.city', 'Unknown') %></p>
<p>Phone: <%= get(user, 'contacts.phone', 'N/A') %></p>
Try it Yourself →
Try/Catch in Templates
For complex logic that might throw errors, wrap it in a try/catch block. This prevents template rendering from failing completely.
<% try { %>
<% const data = JSON.parse(jsonString); %>
<p>Parsed value: <%= data.value %></p>
<% } catch (e) { %>
<p>Unable to display data.</p>
<% } %>
Keep try/catch blocks small and focused. Move complex logic to your route handlers when possible.
Fallback Values with ||
The || operator is the simplest way to provide default values. If the left side is falsy, the right side is used.
<p>Name: <%= user.name || 'Anonymous' %></p>
<p>Role: <%= user.role || 'User' %></p>
<p>Posts: <%= user.postCount || 0 %></p>
Remember that 0, '', and null are all falsy. Use explicit checks when you need to distinguish between them.
<p>Items: <%= typeof cart.count === 'number' ? cart.count : 0 %></p>
Try it Yourself →
Error Boundaries
Wrap sections of your template in error boundaries to gracefully handle failures in specific parts of the page.
<%- include('partials/header') %>
<% try { %>
<%- include('partials/sidebar', { items: menuItems }) %>
<% } catch (e) { %>
<p class="error">Sidebar unavailable</p>
<% } %>
<main>
<%= content %>
</main>
<%- include('partials/footer') %>
This approach ensures that a failure in one component doesn't break the entire page.