Не знаю, для Вас, но для меня htmx всегда ощущался глотком свежего воздуха в плотной атмосфере битвы тяжеловесных фронтэнд-фреймворков. Простой, минималистичный и элегантный - htmx возвращал разработчиков в эпоху семантического веба, истинного Restful создания динамичных веб-приложений.

Как мы помним, одним из важных обещаний при переходе с версии 1.0 на версию 2.0 была заморозка API, т.е. Карсон Гросс, создатель htmx, гарантировал, что больших изменений больше не будет - никогда. Все изменения и дополнения выносились в расширения.

Несмотря на большой интерес и воодушевление со стороны фронтэнд сообщества (статьи на habr: тут, тут, тут и тут), адаптация htmx затормозилось в последние несколько лет из-за некоторых, скажем так, спорных решений и ригидности API. Однако изменение ситуации было маловероятным, что привело к созданию альтернативных HATEOAS фреймворков разной степени успешности.

И поэтому мне было приятно прочитать, что 1 ноября 2025 года Карсон Гросс признался: «Я говорил, что не будет версии 3. Но ничего не говорил про версию 4». Так с юмором началась история htmx 4.0, получившей подзаголовок The fetch()ening.

Главное изменение - переход с древнего XMLHttpRequest на современный fetch(). А заодно - генеральная уборка под капотом библиотеки, которая стала проще, прозрачнее и чище.

Почему снова мажорная версия

Гросс всегда настаивал, что htmx 2.0 будет стабильной и долговечной. Но, поэкспериментировав с мини-библиотекой fixi.js, которую он сам написал для экспериментирования с новыми фичами, Гросс понял: современные возможности fetch() и async/await открывают путь к радикальному упрощению реализации идей htmx.

А раз уж всё равно ломать совместимость, почему бы не заодно избавиться от накопившихся «исторических особенностей»?

“If we’re going to do fetch(), let’s clean house.”

Так и родилась идея htmx 4.0 - без XHR, без спорных наследований и с гораздо более прозрачной логикой.

Главные изменения

1. Прощай, XMLHttpRequest. Да здравствует fetch()

Главный герой апдейта - замена сетевого слоя. Теперь под капотом htmx работает fetch(), а не на XMLHttpRequest. Это изменит внутреннюю модель событий, но для обычного разработчика всё будет по-прежнему:

<button hx-post="/vote" hx-target="#result" hx-swap="outerHTML">
  Голосовать
</button>
<div id="result"></div>

Разница в том, что теперь htmx сможет использовать асинхронные стримы (например, для Server-Sent Events), обрабатывать ошибки и отмены запросов в духе современного JS, и при этом остаться без внешних зависимостей.

2. Атрибуты теперь наследуются явно

Карсон признал свою «самую большую ошибку» - скрытое наследование атрибутов. В 4.0 всё станет предельно честным:

<div hx-target:inherited="#output">
  <button hx-post="/up">Like</button>
  <button hx-post="/down">Dislike</button>
</div>
<output id="output">Выберите вариант...</output>

Если не указать :inherited, кнопки не узнают про hx-target. Больше никаких «магических» эффектов как в CSS - только прозрачность и контроль.

3. История без локального кеша

В htmx 2.0 история страниц сохранялась в localStorage, чтобы ускорить переходы. Но на деле DOM-снапшоты часто ломались из-за сторонних скриптов, скрытых состояний и прочей боли.

Теперь - никакого кеша. При возврате в историю библиотека просто сделает сетевой запрос и подгрузит страницу заново.

Просто. Безопасно. Работает.

Для ностальгирующих (и при необходимости обратной совместимости кода) появится расширение, возвращающее старую модель кэша. Но теперь она не по умолчанию.

Новые возможности

Потоковые ответы и SSE

Переход на fetch() дал доступ к Readable Streams. Теперь htmx сможет обрабатывать потоковые ответы - например, чат, обновления в реальном времени или индикацию прогресса.

<div hx-get="/stream" hx-trigger="load" hx-swap="outerHTML">
  Загрузка...
</div>

Содержимое будет обновляться частями, без полного ререндернига страницы.

Morphing Swap в ядре

В 4.0 появится встроенная поддержка «морфинга» DOM - на основе алгоритма idiomorph, разработанного самим Гроссом. Он умнее классического morphdom и может сам решать, какие узлы сохранить при обновлении.

<div id="profile" hx-swap="morphOuter" hx-get="/user/123"></div>

Благодаря этому интерфейсы будут обновляться плавнее, без мерцаний.

Новый <partial> для частичных обновлений

Out-of-band-вставки теперь будут оформлены цивилизованно - через новый тег <partial>:

<partial hx-target="#sidebar" hx-swap="innerHTML">
  <li>Новый пункт меню</li>
</partial>

Эта конструкция полностью совместима с остальной моделью htmx и делает код читаемым.

View Transitions с очередью

Поддержка View Transitions теперь стала умнее - htmx сам выстраивает очередь анимаций, чтобы они не перебивали друг друга.

CSS-анимации тоже остались, но благодаря async-архитектуре они теперь работают стабильнее.

Новая система событий

Асинхронный движок позволяет гарантировать предсказуемый порядок событий. Новая схема именования:

htmx:<phase>:<system>[:<optional-sub-action>]

Например:

htmx:before:request
htmx:after:swap

Теперь всё логично и линейно.

Улучшенный hx-on

Inline-скрипты через hx-on теперь поддерживают await и контекст ctx:

<button hx-post="/like"
        hx-on:htmx:after:swap="await timeout('3s'); ctx.newContent[0].remove()">
  Убрать элемент через 3 секунды
</button>

Лёгкий способ описать небольшое поведение без стороннего фреймворка.

(Хотя Карсон всё ещё рекомендует Alpine.js для «серьёзных» случаев.)

Что остаётся без изменений

Всё остальное. Ваши hx-get, hx-post, hx-swap, hx-boost, hx-trigger и прочие - живы и здоровы. Более того, 2.0 будет поддерживаться вечно. Если всё работает - можно не обновляться. Но если хочется немного новых фич и чистоты - 4.0 ждёт.

Когда ждать выхода 4.0

  • Альфа: уже доступна - htmx@4.0.0-alpha1

  • Основной релиз: первая половина 2026 года

  • Стабильный релиз (latest): примерно в 2027-м

Следить за процессом и прогрессом можно здесь.

А пока порадуемся, что такой замечательный проект получил новый импульс! А сам Гросс подшутил, что логично пронумеровать возможную грядущую мажорную версию как 8.0.

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