Labs ICT
Pro Login

Best Practices

File organization, naming conventions, and production tips.

File Organization

Keep your templates in a views folder with clear subdirectories. Group related templates together.


views/
  layouts/
    main.ejs
    admin.ejs
  partials/
    header.ejs
    footer.ejs
    sidebar.ejs
  pages/
    home.ejs
    about.ejs
    contact.ejs
  emails/
    welcome.ejs
    reset-password.ejs
  errors/
    404.ejs
    500.ejs
  macros/
    form-elements.ejs
    cards.ejs
    

This structure scales with your app. Anyone joining the project can find templates quickly. Keep naming consistent — lowercase with hyphens for multi-word names.

Naming Conventions

Consistent naming prevents confusion. Follow these patterns.


views/pages/user-profile.ejs      page name: kebab-case
views/partials/nav-bar.ejs        partial name: kebab-case
views/emails/account-created.ejs  descriptive names
views/errors/not-found.ejs        HTTP status as name
    

Use descriptive names that tell you what the template does. user-profile.ejs is better than profile.ejs which could be a product profile, user profile, or anything else.

For partials, prefix with the component name: card-header.ejs, card-body.ejs, card-footer.ejs.

Keep Logic Minimal

Templates should display data, not process it. Move logic to route handlers or helper functions.



<% const total = items.reduce((sum, i) => sum + i.price * i.qty, 0) %>
<% const formatted = '$' + total.toFixed(2) %>

Total: <%= formatted %>

res.render('cart', { items: cart.items, total: cart.getTotal(), formattedTotal: cart.getFormattedTotal() })

Complex expressions make templates hard to read and debug. If a template needs more than simple property access or a single function call, the data should be pre-processed.

Separation of Concerns

Each template should handle one thing. Don't build a template that's a layout, a page, and a component all at once.



// views/partials/product-card.ejs
<%= product.name %>

<%= product.name %>

<%= product.price %>

// views/pages/products.ejs <% products.forEach(product => { %> <%- include('../partials/product-card', { product }) %> <% }) %>

The card partial doesn't know it's on a products page. The products page doesn't know how cards are styled. Each piece is independent and reusable.

Try it Yourself →

Escape by Default

Use <%= to escape HTML. Only use <%- when you intentionally want raw HTML.



<%= userInput %>

<%- userInput %>

<%- include('partials/header') %> <%- defineContent() %>

XSS vulnerabilities happen when unescaped user input enters the page. Always ask yourself: "Is this data trusted?" If not, escape it.

Production Tips

A few settings make EJS faster and more reliable in production.


// Disable debugging in production
app.set('view cache', true)

// Set the views directory once
app.set('views', './views')

// Set the template engine
app.set('view engine', 'ejs')

// Set default render options
app.locals.pretty = false
app.locals.cache = true
    

Template caching is critical in production. Without it, EJS reads and compiles templates on every request. Enable caching and your response times drop significantly.


// Environment-specific settings
if (process.env.NODE_ENV === 'production') {
  app.set('view cache', true)
  ejs.cache = new (require('lru-cache'))(100)
}

if (process.env.NODE_ENV === 'development') {
  app.set('view cache', false)
  app.set('strict routing', false)
}
    

In development, disable caching so template changes show up immediately. In production, enable everything that improves performance.

Avoid Common Mistakes

Here are patterns that cause problems in production.



<%= undefinedVar.property %>

<%= typeof undefinedVar !== 'undefined' ? undefinedVar.property : 'N/A' %>

<% if (user) { %> <% if (user.role === 'admin') { %> <% if (user.permissions.includes('edit')) { %> <% } %> <% } %> <% } %> <%- include('partials/admin-controls', { user }) %> <%- include('layouts/main', { body: content }) %>

Small, focused templates are easier to test, debug, and maintain. When a template exceeds 100 lines, consider splitting it.