Разработка сайта на 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("ru-RU").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('ru') }}">
<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: "ru",
  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"

Сроки

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