Общие части страниц
У всех страниц блога одинаковые шапка, меню и подвал. Копировать их в каждый шаблон нельзя: правку пришлось бы делать во всех файлах. Частичный шаблон (partial) — кусок HTML в отдельном файле, который вставляется в другие шаблоны функцией include().
Шапка и подвал
Создайте папку views/partials/ и в ней два файла. Шапка views/partials/header.ejs:
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title><%= title %> — <%= siteName %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header>
<a href="/posts"><%= siteName %></a>
<nav>
<a href="/posts">Статьи</a>
<a href="/about">О блоге</a>
</nav>
</header>
<main>И подвал views/partials/footer.ejs:
</main>
<footer>Блог об Express, 2025</footer>
</body>
</html>Шапка открывает теги <html>, <body> и <main>, а подвал их закрывает.
Подключение
Перепишите views/posts/index.ejs так, чтобы в нём осталось только содержимое страницы:
<%- include('../partials/header') %>
<h1><%= title %></h1>
<ul>
<% for (const post of posts) { %>
<li><a href="/posts/<%= post.id %>"><%= post.title %></a></li>
<% } %>
</ul>
<%- include('../partials/footer') %>Путь в include() считается от папки текущего шаблона, расширение можно не писать. Здесь нужна вставка <%-: шаблон уже готовый HTML, и экранировать его нельзя. Откройте /posts — у страницы появятся меню и подвал, а во вкладке браузера будет «Все статьи — Блог об Express».
Страница статьи
Второй шаблон собирается так же. Создайте views/posts/show.ejs:
<%- include('../partials/header') %>
<article>
<h1><%= post.title %></h1>
<p><%= post.body %></p>
</article>
<a href="/posts">Все статьи</a>
<%- include('../partials/footer') %>В контроллере show замените ответ на res.render('posts/show', { title: post.title, post }). Страница /posts/1 получит ту же шапку и подвал.
Передача данных в partial
Partial видит все переменные страницы, которая его подключила. Можно передать и свои — вторым аргументом:
<%- include('../partials/postCard', { post }) %>Так удобно вынести карточку статьи views/partials/postCard.ejs и использовать её в списке, в поиске и на главной.
Частая ошибка
Partial использует переменную title, поэтому её нужно передавать в каждый res.render(). Если забыть, шаблон упадёт с ошибкой title is not defined. Либо всегда передавайте title, либо задайте значение по умолчанию в middleware: res.locals.title = 'Блог' — тогда страницы без своего заголовка получат общий.