lenec ru

← все посты

htmx: интерактивность без JavaScript-фреймворков — когда это работает

10K

Последние десять лет фронтенд двигался в одном направлении: JSON API на бэкенде, SPA-фреймворк на клиенте, тонны JavaScript для рендеринга того, что раньше делал сервер. htmx предлагает откатиться к основам — пусть сервер возвращает HTML, а браузер просто вставляет его в нужное место. Без React, без Vue, без бандлера. Одна библиотека в 14 КБ — и серверный шаблон становится интерактивным.

Философия: hypermedia вместо JSON API + SPA

В SPA-подходе сервер — JSON API, логика отображения на клиенте. htmx возвращает к модели hypermedia-driven application: сервер отвечает фрагментами HTML, браузер подставляет их в DOM.

  • Логика отображения на сервере — один источник правды.
  • Любой серверный язык: Go, Python, Ruby, PHP — без привязки к Node.js.
  • Нет этапа сборки. Нет node_modules. Нет webpack/vite конфигов.
  • SEO из коробки — сервер отдаёт готовый HTML.

Основные атрибуты: hx-get, hx-post, hx-swap, hx-trigger

htmx расширяет HTML атрибутами, превращающими любой элемент в AJAX-запрос:

<!-- Загрузить контент по клику -->
<button hx-get="/api/users" hx-target="#user-list" hx-swap="innerHTML">
  Загрузить пользователей
</button>
<div id="user-list"></div>

<!-- Отправить форму без перезагрузки -->
<form hx-post="/api/comments" hx-target="#comments" hx-swap="beforeend">
  <input name="text" placeholder="Комментарий..." />
  <button type="submit">Отправить</button>
</form>

<!-- Удалить элемент с подтверждением -->
<button hx-delete="/api/posts/42"
        hx-confirm="Удалить пост?"
        hx-target="closest .post"
        hx-swap="outerHTML">
  Удалить
</button>

Ключевые атрибуты:

  • hx-get/post/put/delete — HTTP-метод и URL.
  • hx-target — CSS-селектор элемента для вставки ответа.
  • hx-swap — как вставить: innerHTML, outerHTML, beforeend, afterbegin.
  • hx-trigger — событие: click, submit, keyup, intersect, load.

Паттерны: infinite scroll, inline editing, search

Infinite scroll через IntersectionObserver:

<div id="posts">
  <article>...</article>
  <div hx-get="/posts?page=2"
       hx-trigger="intersect once"
       hx-target="#posts"
       hx-swap="beforeend">
    Загрузка...
  </div>
</div>

Search-as-you-type с debounce:

<input type="search" name="q"
       hx-get="/search"
       hx-trigger="keyup changed delay:300ms"
       hx-target="#results" />
<div id="results"></div>

Inline editing — клик превращает текст в форму:

<span hx-get="/edit/title/42"
      hx-trigger="click"
      hx-swap="outerHTML">
  Название статьи
</span>

Сервер возвращает форму, после сабмита — обратно текст. Цикл замыкается без единой строки JS.

Архитектура: htmx + серверные шаблоны

htmx работает с любым серверным стеком. Пример на Go:

func SearchHandler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query().Get("q")
    users := db.SearchUsers(query)
    // Возвращаем HTML-фрагмент, не полную страницу
    tmpl.ExecuteTemplate(w, "user-list.html", users)
}
<!-- templates/user-list.html -->
{{range .}}
<div class="user-card">
  <img src="{{.Avatar}}" />
  <span>{{.Name}}</span>
</div>
{{end}}

Аналогично с Python/Jinja2, Ruby/ERB, PHP/Blade. Правило: эндпоинты для htmx возвращают HTML-фрагменты, а не JSON.

Когда htmx не подходит

  • Сложный клиентский стейт. Drag-and-drop, real-time коллаборация, оффлайн — нужен клиентский фреймворк.
  • Высокая частота обновлений. Чат с сотнями сообщений в секунду, биржевые тикеры — latency на каждый запрос.
  • Сложные формы. Многошаговые визарды с зависимыми полями проще на клиенте.
  • Оффлайн. htmx требует сервер для каждого взаимодействия.

Где htmx идеален: админки, CRUD-интерфейсы, контентные сайты с формами, дашборды, внутренние инструменты — везде, где интерактивность сводится к «отправь запрос — покажи результат».

Вывод

htmx — не шаг назад, а осознанный выбор простоты. Если приложение — серверный рендеринг с точечной интерактивностью, htmx уберёт тысячи строк JavaScript и десятки зависимостей. Не каждому проекту нужен React.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.