Розробка сайту на Eleventy (Static Site Generator)

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка сайту на Eleventy (Static Site Generator)
Середня
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка сайту на Eleventy (Static Site Generator)

Eleventy (11ty) — генератор статичних сайтів на Node.js, створений Zachem Leathermanом з Netlify. Принципіальна відмінність від Hugo та Jekyll: JavaScript як мова конфігурації та розширення, підтримка 10+ мов шаблонів (Nunjucks, Liquid, Handlebars, Mustache, EJS, HAML, Pug, WebC) та нульовий JavaScript на клієнті за замовчуванням. Це робить Eleventy гнучким інструментом для всього — від документації до великих маркетингових сайтів.

Архітектура проекту

mysite/
├── .eleventy.js          # Конфігурація (або eleventy.config.js)
├── src/
│   ├── _data/            # Глобальні дані (JS, JSON, YAML)
│   │   ├── site.js
│   │   ├── navigation.json
│   │   └── team.yaml
│   ├── _includes/        # Перевикористовуючи компоненти
│   │   ├── layouts/
│   │   │   ├── base.njk
│   │   │   └── post.njk
│   │   └── components/
│   │       ├── card.njk
│   │       └── hero.njk
│   ├── blog/             # Колекція постів
│   │   ├── blog.json     # Cascade data для всієї папки
│   │   └── *.md
│   ├── services/
│   ├── assets/
│   │   ├── css/
│   │   └── js/
│   └── index.njk
├── package.json
└── _site/                # Результат сборки

Конфігурація eleventy.config.js

const { EleventyHtmlBasePlugin } = require("@11ty/eleventy");
const pluginRss = require("@11ty/eleventy-plugin-rss");
const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
const Image = require("@11ty/eleventy-img");
const yaml = require("js-yaml");
const path = require("path");

module.exports = function(eleventyConfig) {

  // Плагіни
  eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
  eleventyConfig.addPlugin(pluginRss);
  eleventyConfig.addPlugin(pluginSyntaxHighlight, {
    preAttributes: { tabindex: 0 }
  });

  // Парсер YAML для _data
  eleventyConfig.addDataExtension("yaml,yml", contents => yaml.load(contents));

  // Passthrough copy
  eleventyConfig.addPassthroughCopy("src/assets/fonts");
  eleventyConfig.addPassthroughCopy({ "src/assets/images/favicon": "/" });

  // Фільтри
  eleventyConfig.addFilter("dateFormat", function(date, format = "dd.MM.yyyy") {
    return new Intl.DateTimeFormat("uk-UA").format(new Date(date));
  });

  eleventyConfig.addFilter("readingTime", function(content) {
    const words = content.split(/\s+/).length;
    const minutes = Math.ceil(words / 200);
    return `${minutes} хв читання`;
  });

  eleventyConfig.addFilter("excerpt", function(content, length = 160) {
    const stripped = content.replace(/<[^>]*>/g, '');
    return stripped.length > length
      ? stripped.substring(0, length).trim() + '…'
      : stripped;
  });

  // Async Image Shortcode
  eleventyConfig.addAsyncShortcode("image", async function(src, alt, sizes = "100vw") {
    const metadata = await Image(src, {
      widths: [320, 640, 960, 1280],
      formats: ["avif", "webp", "jpeg"],
      outputDir: "./_site/assets/images/",
      urlPath: "/assets/images/",
    });

    const imageAttributes = {
      alt,
      sizes,
      loading: "lazy",
      decoding: "async",
    };

    return Image.generateHTML(metadata, imageAttributes);
  });

  // Колекції
  eleventyConfig.addCollection("blog", function(collectionApi) {
    return collectionApi.getFilteredByGlob("src/blog/*.md")
      .filter(post => !post.data.draft)
      .reverse();
  });

  eleventyConfig.addCollection("tagList", function(collectionApi) {
    const tagSet = new Set();
    collectionApi.getAll().forEach(item => {
      (item.data.tags || []).forEach(tag => {
        if (!["post", "all"].includes(tag)) tagSet.add(tag);
      });
    });
    return [...tagSet].sort();
  });

  // Налаштування Markdown
  const markdownIt = require("markdown-it");
  const markdownItAnchor = require("markdown-it-anchor");
  const markdownItAttrs = require("markdown-it-attrs");

  const md = markdownIt({ html: true, linkify: true, typographer: true })
    .use(markdownItAnchor, {
      permalink: markdownItAnchor.permalink.ariaHidden({ placement: "after" }),
      slugify: s => s.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '')
    })
    .use(markdownItAttrs);

  eleventyConfig.setLibrary("md", md);

  // Конфіг директорій
  return {
    dir: {
      input: "src",
      output: "_site",
      includes: "_includes",
      data: "_data",
    },
    htmlTemplateEngine: "njk",
    markdownTemplateEngine: "njk",
    templateFormats: ["md", "njk", "html"],
  };
};

Шаблони Nunjucks

{# src/_includes/layouts/base.njk #}
<!DOCTYPE html>
<html lang="{{ site.lang | default('uk') }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{% if title %}{{ title }} | {{ site.title }}{% else %}{{ site.title }}{% endif %}</title>
  <meta name="description" content="{{ description | default(site.description) }}">
  <meta property="og:title" content="{{ title | default(site.title) }}">
  <meta property="og:url" content="{{ site.url }}{{ page.url }}">
  <link rel="canonical" href="{{ site.url }}{{ page.url }}">
  <link rel="stylesheet" href="/assets/css/main.css">
</head>
<body>
  {% include "components/header.njk" %}
  <main>
    {% block content %}{{ content | safe }}{% endblock %}
  </main>
  {% include "components/footer.njk" %}
  <script src="/assets/js/main.js" defer></script>
</body>
</html>
{# src/_includes/layouts/post.njk #}
---
layout: layouts/base.njk
---
<article class="post">
  <header>
    <h1>{{ title }}</h1>
    <time datetime="{{ date | htmlDateString }}">
      {{ date | dateFormat }}
    </time>
    <span class="reading-time">{{ content | readingTime }}</span>
  </header>

  {% if image %}
  {% image image, title, "(max-width: 768px) 100vw, 1200px" %}
  {% endif %}

  <div class="post__body">{{ content | safe }}</div>

  {% if tags %}
  <ul class="tags">
    {% for tag in tags %}
    {% if tag != "post" %}
    <li><a href="/tags/{{ tag | slug }}/">#{{ tag }}</a></li>
    {% endif %}
    {% endfor %}
  </ul>
  {% endif %}
</article>

Data Cascade

Eleventy підтримує каскад даних — пріоритет від глобального до локального:

// src/_data/site.js — глобальні дані
module.exports = {
  title: "Назва компанії",
  url: process.env.SITE_URL || "https://example.com",
  lang: "uk",
  description: "Опис сайту",
  author: {
    name: "Команда",
    email: "[email protected]"
  }
};
// src/blog/blog.json — дані для всієї папки blog/
{
  "layout": "layouts/post.njk",
  "tags": ["post"],
  "permalink": "/blog/{{ page.fileSlug }}/"
}

Front matter окремого посту перекриває дані папки.

Пагінація

{# src/blog/index.njk #}
---
title: Блог
pagination:
  data: collections.blog
  size: 12
  alias: posts
  reverse: true
permalink: "/blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber + 1 }}/{% endif %}"
---
<div class="posts-grid">
  {% for post in posts %}
  {% include "components/post-card.njk" %}
  {% endfor %}
</div>

{% if pagination.pages.length > 1 %}
<nav class="pagination">
  {% if pagination.href.previous %}
  <a href="{{ pagination.href.previous }}">← Назад</a>
  {% endif %}

  <span>{{ pagination.pageNumber + 1 }} / {{ pagination.pages.length }}</span>

  {% if pagination.href.next %}
  <a href="{{ pagination.href.next }}">Вперед →</a>
  {% endif %}
</nav>
{% endif %}

Інтеграція з Vite

// vite.config.js
export default {
  build: {
    outDir: '_site/assets',
    emptyOutDir: false,
    rollupOptions: {
      input: { main: 'src/assets/js/main.js' }
    }
  }
}

Запуск паралельно: concurrently "eleventy --serve" "vite build --watch"

Терміни

Сайт на стартовому шаблоні з кастомним контентом — 4–6 днів. Розробка з нуля з кастомними колекціями, пагінацією, оптимізацією зображень, CI/CD — 2–3 тижні. Великий портал з десятками типів контенту, мультимовністю, інтеграціями з CMS — 1–2 місяці.