Matrix-поля Craft CMS не відображаються коректно, Live Preview видає помилки, а сторінки завантажуються з LCP > 4 с. Стандартні шаблони часто не справляються зі складною структурою контенту. Замовте кастомні Twig-шаблони та отримайте перші результати вже через 4 дні. Ми вирішили цю проблему для понад 50 проєктів — від корпоративних сайтів до медіа-порталів із тисячами записів. Нижче розберемо, які технічні складнощі усувають кастомні Twig-шаблони та як їх розробка окупається.
Чому кастомні Twig-шаблони швидші за стандартні?
Кастомні шаблони дозволяють повністю контролювати генерацію HTML. У стандартних шаблонах часто зустрічаються N+1 запити — цикл із зверненнями до пов'язаних елементів. Як зазначено в документації Craft CMS: Eager-loading drastically reduces query count. Ми використовуємо eager-loading через with(), що скорочує кількість SQL-запитів у 3–5 разів. Також застосовуємо кешування {% cache %} та оптимізуємо зображення через Image Transforms із WebP і srcset. У результаті TTFB падає до 200–400 мс, а LCP виявляється нижче 2.5 с.
Наші кастомні Twig-шаблони для Craft CMS завантажуються в 2-3 рази швидше за стандартні, що підтверджується тестами Core Web Vitals.
Які проблеми вирішуємо
N+1 запитів та повільне завантаження
У стандартних шаблонах часто зустрічається звернення до пов'язаних елементів всередині циклу — наприклад, post.categories.all(). Кожен виклик створює окремий SQL-запит. Ми використовуємо with() та eager-loading прямо в контролері або в entry-типі. Результат — TTFB падає до 200–400 мс, а загальна продуктивність зростає.
Неповне відображення Matrix-блоків
Якщо в адмінці налаштовано 10 типів блоків, а шаблон обробляє лише 3 — контент-менеджери не зможуть використовувати решту. Ми реалізуємо повний switch по кожному block.type, з окремими partial-компонентами та стилями.
Помилки в SEO-мета-тегах
Відсутність Open Graph, невірний alt для зображень, дублювання title — часті проблеми. У кастомних шаблонах ми задаємо {% block meta %} для кожної сторінки, динамічно формуємо description та зображення. Наприклад, у зображень прописуємо alt="кастомний Twig шаблон Craft CMS", щоб покращити семантику.
Як ми це робимо: кейс із нашої практики
Наш клієнт — медіа-портал із 5000+ записів. Ми замінили стандартні шаблони на кастомні. Спершу сторінка категорії виконувала 78 SQL-запитів і завантажувалася за 6 секунд. Після рефакторингу з eager-loading та кешуванням кількість запитів скоротилася до 12, а LCP знизився до 1.8 с — це в 3,3 рази швидше. Додатково налаштували Image Transforms для WebP і додали lazy-load для зображень. Контент-менеджери тепер можуть використовувати всі 8 типів Matrix-блоків без доробок. Клієнт заощадив близько 30% бюджету на хостингу завдяки зменшенню навантаження.
Покрокова інструкція з налаштування кастомного шаблону
- Аналіз структури контенту: вивчаємо entry-типи, Matrix-поля, типи відносин.
- Проектування layout-ів: створюємо
_layouts/(base, default, fullwidth) та_components/(header, footer). - Реалізація з eager-loading: у контролерах і шаблонах використовуємо
with()для підвантаження пов'язаних елементів. - Налаштування Matrix-блоків: пишемо
switchдля кожного типу блоку з окремим partial-компонентом. - Кешування: застосовуємо
{% cache %}для часто запитаних блоків (навігація, сайдбари). - Тестування Live Preview: перевіряємо всі entry-типи та мобільну верстку.
- Дефолтна оптимізація зображень: налаштовуємо WebP та srcset через Image Transforms.
Результати порівняння стандартного та кастомного шаблону
| Критерій | Стандартний шаблон | Кастомний шаблон |
|---|---|---|
| Швидкість завантаження | TTFB > 1 с | TTFB < 400 мс |
| Кількість SQL-запитів | 50–80 | 10–15 |
| Підтримка Matrix-полів | Часткова | Повна |
| SEO-мета-теги | Базові | Open Graph, Schema, Microdata |
| Адаптивність зображень | Відсутня | WebP + srcset |
Що входить у роботу
- Вихідні коди всіх шаблонів (Twig).
- Налаштовані Image Transforms (якщо потрібні).
- Документація по макросах та partial-компонентах.
- Інструкція з розгортання.
- 14 днів гарантійної підтримки після деплою.
- Навчання контент-менеджерів роботі з Matrix-полями (при необхідності).
Процес роботи
| Етап | Тривалість | Що робимо |
|---|---|---|
| Аналітика | 1 день | Вивчаємо структуру контенту: entry-типи, Matrix-поля, типи відносин. Складаємо карту URI та маппінг шаблонів |
| Проектування | 1-2 дні | Створюємо _layouts/ (base, default, fullwidth) та _components/ (header, footer, post-card). Визначаємо макроси для повторюваних елементів |
| Реалізація | 2-4 дні | Пишемо кожен шаблон, використовуємо eager-loading та кешування {% cache %}. Налаштовуємо Live Preview для всіх entry-типів |
| Тест | 1 день | Перевіряємо всі URI, мобільну верстку, мета-теги, зображення. Виправляємо помилки Dev Mode |
| Деплой і підтримка | 14 днів | Передаємо код у Git, налаштовуємо CI/CD. Виправляємо зауваження безкоштовно |
Як ми налаштовуємо Live Preview для всіх entry-типів?
Для кожного entry-типу створюємо окремий шаблон _entry.twig у відповідній папці. У ньому наслідуємо базовий layout та перевизначаємо блоки content і meta. Live Preview підхоплює правильний шаблон автоматично, якщо дотримана структура папок templates/ з іменем entry-типу. Додатково перевіряємо, що craft.app.request.getSegment не викликає помилок при прев'ю.
Чому варто замовити кастомні шаблони у нас?
Понад 5 років досвіду з Craft CMS — ми знаємо, як працюють pagination, Image Transforms, globals та relations. Кожен проєкт отримує гарантію сумісності з останньою стабільною версією Craft CMS та PHP 8.1+. Оптимізація Core Web Vitals: LCP < 2.5 с завдяки fetchpriority та lazy-load. Економія до 40% на підтримці за рахунок спрощення шаблонів. Повна документація по кожному макросу та partial-компоненту. Для подробиць щодо eager-loading дивіться документацію Craft CMS. Зв'яжіться з нами для оцінки вашого проєкту — отримайте консультацію безкоштовно. Середній чек проєкту — 1500$.
Приклади коду
Базова структура шаблонів:
templates/
├── _layouts/
│ ├── base.twig
│ ├── default.twig
│ └── fullwidth.twig
├── _components/
│ ├── header.twig
│ ├── footer.twig
│ ├── post-card.twig
│ └── breadcrumbs.twig
├── _macros/
│ └── helpers.twig
├── index.twig
├── 404.twig
├── blog/
│ ├── index.twig
│ └── _entry.twig
└── services/
├── index.twig
└── _entry.twig
Базовий layout (base.twig):
<!DOCTYPE html>
<html lang="{{ craft.app.language }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{{ siteName }}{% endblock %}</title>
{% block meta %}{% endblock %}
{{ craft.vite.stylesheet('src/css/app.pcss') }}
</head>
<body class="{% block bodyClass %}{% endblock %}">
{% include '_components/header' %}
<main id="main" tabindex="-1">
{% block content %}{% endblock %}
</main>
{% include '_components/footer' %}
{{ craft.vite.script('src/js/app.ts') }}
{% block scripts %}{% endblock %}
</body>
</html>
Обробка Matrix-блоків:
<div class="post-body">
{% for block in entry.pageContent.all() %}
{% switch block.type %}
{% case 'richText' %}
<div class="prose">{{ block.body }}</div>
{% case 'pullQuote' %}
<blockquote>
<p>{{ block.quote }}</p>
{% if block.attribution %}<cite>{{ block.attribution }}</cite>{% endif %}
</blockquote>
{% case 'imageBlock' %}
{% set img = block.image.one() %}
{% if img %}
<figure>
<img src="{{ img.getUrl({ width: 1200 }) }}" alt="{{ img.alt }}">
{% if block.caption %}<figcaption>{{ block.caption }}</figcaption>{% endif %}
</figure>
{% endif %}
{% case 'codeSnippet' %}
<pre><code class="language-{{ block.language }}">{{ block.code | escape }}</code></pre>
{% endswitch %}
{% endfor %}
</div>
Макроси та Image Transforms:
// config/image-transforms.php
return [
'thumbnail' => ['width' => 400, 'height' => 300, 'mode' => 'crop'],
'large' => ['width' => 1200, 'height' => 630, 'mode' => 'crop'],
'avatar' => ['width' => 100, 'height' => 100, 'mode' => 'crop', 'position' => 'center-center'],
'ogImage' => ['width' => 1200, 'height' => 630, 'mode' => 'crop', 'format' => 'jpg'],
];
{# templates/_macros/helpers.twig #}
{% macro truncate(text, length = 150) %}
{% if text | length > length %}
{{ text | slice(0, length) }}…
{% else %}
{{ text }}
{% endif %}
{% endmacro %}
{% macro breadcrumbs(entry) %}
<nav aria-label="Хлібні крихти">
<a href="/">Головна</a>
{% for ancestor in entry.getAncestors() %}
<a href="{{ ancestor.url }}">{{ ancestor.title }}</a>
{% endfor %}
<span aria-current="page">{{ entry.title }}</span>
</nav>
{% endmacro %}
Всі шаблони проходять код-рев'ю та оптимізуються під Core Web Vitals. Якщо у вас нестандартна бізнес-логіка — додамо підтримку custom fields, relations та events. Замовте кастомні шаблони та переконайтеся в їх ефективності.







