Як кастомні Twig-шаблони Craft CMS усувають N+1 та оптимізують LCP

Стандартні шаблони Craft CMS часто не справляються зі складною структурою контенту: сторінки завантажуються повільно, а Matrix-поля відображаються некоректно. Ми розробляємо кастомні Twig-шаблони, які повністю контролюють генерацію HTML та усувають N+1 запити. Наша команда реалізує проєкт під ключ — від аудиту до впровадження, забезпечуючи стабільну роботу та подальшу підтримку.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Як кастомні Twig-шаблони Craft CMS усувають N+1 та оптимізують LCP
Середній
~5 днів
Часті запитання

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% бюджету на хостингу завдяки зменшенню навантаження.

Покрокова інструкція з налаштування кастомного шаблону

  1. Аналіз структури контенту: вивчаємо entry-типи, Matrix-поля, типи відносин.
  2. Проектування layout-ів: створюємо _layouts/ (base, default, fullwidth) та _components/ (header, footer).
  3. Реалізація з eager-loading: у контролерах і шаблонах використовуємо with() для підвантаження пов'язаних елементів.
  4. Налаштування Matrix-блоків: пишемо switch для кожного типу блоку з окремим partial-компонентом.
  5. Кешування: застосовуємо {% cache %} для часто запитаних блоків (навігація, сайдбари).
  6. Тестування Live Preview: перевіряємо всі entry-типи та мобільну верстку.
  7. Дефолтна оптимізація зображень: налаштовуємо 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. Замовте кастомні шаблони та переконайтеся в їх ефективності.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060