The Include-Based Layout Pattern
EJS doesn't have built-in layout inheritance like some other engines, but you can achieve the same thing with includes. The idea is simple: have a base template that includes header and footer, and your page content gets injected in the middle.
<%- include('partials/header') %>
<%= title %>
<%= content %>
<%- include('partials/footer') %>
This gives you a consistent layout across all your pages without repeating the header and footer HTML.
The Body Pattern
For more flexibility, use a "body" pattern where your layout includes a variable content section:
<%- include('partials/header') %>
<%- body %>
<%- include('partials/footer') %>
Then in your route, you render the page content first and pass it to the layout. This separates your layout from page content completely.
Try it Yourself →Extending Layouts
You can create nested layouts by including one layout inside another. Start with a base layout, then create page-specific layouts that include it:
<%# base.ejs - the foundation %>
<%- include('head') %>
<%- body %>
<%- include('scripts') %>
<%# admin.ejs - extends base %>
<%- include('base', { body: `
<%- include('admin-header') %>
<%- adminContent %>
`}) %>
This creates a hierarchy: base layout → admin layout → specific page. Each level adds its own pieces.
Partial Headers and Footers
Break your layout into logical partials. Each partial handles one concern:
<%# partials/head.ejs %>
<%= title %>
<%# partials/footer.ejs %>
Keep each partial focused on one thing. Your main template stays clean and your code stays maintainable.