htmx: живые интерфейсы на HTML и PHP — мастерская и 8 примеров
Строим динамические интерфейсы через HTML-ответы PHP. Восемь живых задач, инспектор запросов, ошибки форм и готовый проект для локального запуска и Linux VPS.

htmx позволяет сделать поиск, форму, фильтры и редактирование прямо в странице, сохранив обычную серверную разработку на PHP. Пользователь нажимает кнопку или меняет поле, браузер отправляет запрос, сервер возвращает небольшой HTML-фрагмент, а библиотека вставляет его в нужное место. Вся страница при этом не перезагружается.
Ниже работает мини-мастерская с восемью связанными интерфейсными задачами. Здесь можно переименовать заявку, найти её по новому названию, получить ошибки формы, загрузить продолжение списка, отправить запись в архив и вернуть её. Ответы действительно формирует PHP на сервере. В инспекторе видны HTTP-статус, время запроса и полученный HTML, а в исходниках — тот же проект для Windows, Linux и размещения на VPS.
Открыть мастерскую на весь экран ↗Скачать PHP-проект и исходники ↓
Что вы получите: динамический интерфейс вокруг PHP
Представьте внутреннюю панель небольшой команды. Сотруднику нужно быстро найти заявку, изменить заголовок, отфильтровать список и посмотреть состояние подготовки отчёта. Ради этих операций необязательно сразу строить отдельный клиент с большим хранилищем состояния. Если приложение уже умеет формировать страницы и проверять формы, часть ответов можно превратить в самостоятельные HTML-фрагменты.
Практический результат htmx — короткая связь между элементом интерфейса и серверным действием. Кнопка знает адрес запроса, поле поиска — момент отправки, область результатов — способ обновления. При этом правила данных остаются в PHP: какие записи показывать, какие значения допустимы, можно ли выполнить операцию. Такой подход особенно удобен, когда большая часть работы связана с формами, списками, карточками и обычным CRUD.
Как устроен обмен: HTML приходит с сервера
В мастерской сервер хранит учебный набор заявок в отдельной сессии посетителя. Поиск получает строку q, выбирает подходящие записи и собирает карточки. htmx получает готовую разметку и обновляет контейнер результатов. Браузеру не нужно заново строить карточку из JSON: шаблон находится рядом с серверной логикой.
Это не означает, что JSON становится ненужным. Один сервис может иметь HTML-маршруты для собственного интерфейса и JSON API для интеграций. В нашей мастерской экспорт — отдельный JSON-файл, который пользователь скачивает обычной ссылкой. Для обновления интерфейса используется HTML. Важно заранее договориться о формате каждого ответа, чтобы неожиданно не показать сырой JSON внутри формы или страницу входа внутри таблицы.
Официальные источники: Документация htmx 4, hx-get.
Где применять htmx и какую задачу она упрощает
| Задача | Как выглядит действие | Результат для пользователя |
|---|---|---|
| Поиск и фильтры | GET с параметрами возвращает карточки | Нужные записи появляются после короткой паузы |
| Личный кабинет | Форма возвращается с ошибками или результатом | Введённые значения не пропадают |
| Административная панель | Карточка переключается между просмотром и формой | Небольшая правка выполняется на месте |
| Каталог и журнал | Сервер выдаёт следующую порцию | Список растёт без полной перезагрузки |
| Состояние операции | Небольшой фрагмент запрашивается до завершения | Видно движение задачи и окончательный результат |
Особенно естественно htmx встраивается в существующий PHP-проект, где уже есть авторизация, шаблоны и маршруты. Можно начать с одного поиска или одной формы и оценить результат. Для такого постепенного изменения не требуется переносить весь интерфейс одновременно. Сложность определяется задачей: маленькая форма останется маленькой, а запутанные серверные правила всё равно придётся привести в порядок.
Почему в примерах htmx 4.0.1 и чем она отличается от 2.x
В проекте явно закреплена htmx 4.0.1. На 7 ноября 2026 года официальный GitHub помечает этот релиз как Latest, а отдельная документация four.htmx.org описывает ветку 4. При этом в npm тег latest указывает на 2.0.11, а next — на 4.0.1. Поэтому команда установки без номера версии и скачанный проект могут дать разные поколения библиотеки. Здесь версия выбрана осознанно, и все примеры относятся именно к 4.0.1.
У четвёртой ветки изменились важные соглашения: наследование атрибутов по умолчанию стало явным, ответы 4xx и 5xx теперь могут подменять содержимое, а имена событий приведены к другому формату. Например, используется htmx:after:request и объект event.detail.ctx. Старый обработчик из статьи про 2.x нельзя переносить механически. При обновлении своего проекта закрепите версию, проверьте формы, историю, расширения и поведение ошибок на тестовом окружении.
Официальные источники: Релиз htmx 4.0.1, Версии и dist-tags npm, Что изменилось в htmx 4.
Мини-мастерская: восемь живых интерфейсов
Начните с поиска и общего списка, затем измените заявку #1 во вкладке редактирования. Вернитесь в поиск и введите новое название: так видно, что разные интерфейсы работают с общими серверными данными. После этого отправьте неверную форму, создайте правильную заявку и попробуйте обратимую архивацию. Общий счётчик изменяется вместе с состоянием набора.
Под каждым примером раскрывается код, который можно скопировать или скачать. Ниже расположен инспектор последних восьми запросов. Раскройте запись и сравните HTML с тем, что появилось на экране. Сброс восстанавливает только вашу копию учебного набора. Экспорт сохраняет синтетические записи в JSON; email из формы проверяется, но не хранится и никуда не отправляется.
Открыть мастерскую на весь экран ↗Скачать PHP-проект и исходники ↓
Пять атрибутов, с которых удобно начать
| Атрибут | На какой вопрос отвечает | Пример |
|---|---|---|
| hx-get / hx-post | Куда отправить запрос и каким методом | Получить список или сохранить форму |
| hx-trigger | Когда отправлять | После изменения поля и паузы |
| hx-target | Какую область обновить | Контейнер результатов поиска |
| hx-swap | Как вставить ответ | Заменить содержимое или элемент целиком |
| hx-sync | Как согласовать запросы | Отменить устаревший поиск при новом запросе |
Начинайте с этой последовательности: действие, маршрут, ответ, область обновления. Если кнопка отправляет правильный запрос, но результат появляется не там, проверьте target. Если после обновления исчезает контейнер или дублируется его ID, проверьте swap. Для innerHTML сервер возвращает содержимое контейнера, для outerHTML — новый контейнер целиком. Согласование этой пары устраняет многие ошибки без дополнительного JavaScript.
Официальные источники: hx-post, hx-target, hx-swap.
Подключение библиотеки и устройство исходников
htmx поставляется одним JavaScript-файлом. В нашем проекте он хранится локально вместе с Bootstrap 5.3.8 и стилями мастерской, поэтому открытая страница не зависит от стороннего CDN. Сборщик для запуска PHP-примеров не нужен. Если библиотека устанавливается через npm в существующее приложение, укажите точную версию; зависимости и сборка такого приложения остаются вашей отдельной инфраструктурой.
<script src="assets/htmx.min.js" defer></script>
<script src="assets/workshop.js" defer></script>
Альтернатива для проекта, где уже используется npm
npm install htmx.org@4.0.1
В public находятся index.php, api.php, общие функции рендера, локальные стили и библиотека. Маршруты api.php ограничены учебными действиями. Compose поднимает PHP с Apache, а отдельный deploy/compose.server.yaml добавляет Caddy для VPS. Инструкции, лицензии и воспроизводимая HTTP-проверка лежат рядом с кодом. Секретные env-файлы и реальные данные в архив не включаются.
Windows PowerShell: запуск и первая проверка
Установите Docker Desktop, включите Linux-контейнеры и дождитесь работающего Engine. Команда docker version должна показать серверную часть. Ни PHP, ни Apache отдельно ставить не нужно. Ниже используется новый каталог C:\Projects\htmx-lab; если архив уже распакован, перейдите в его папку htmx-project и начните с проверки Compose. Не перезаписывайте рабочую папку другого проекта.
New-Item -ItemType Directory -Force C:\Projects | Out-Null
Set-Location C:\Projects
Invoke-WebRequest https://ae-nekrasov.ru/wp-content/plugins/aen-htmx-workshop/htmx-project.zip -OutFile htmx-project.zip
Expand-Archive htmx-project.zip -DestinationPath htmx-lab
Set-Location .\htmx-lab\htmx-project
docker compose config --quiet
docker compose up -d --build --wait
docker compose ps
Invoke-RestMethod http://127.0.0.1:8101/health.php
Откройте http://127.0.0.1:8101/index.php и выполните действия в мастерской. Ожидается healthy-сервис и JSON status: ok на health.php. Если 8101 занят, замените только внешний порт в compose.yaml и используйте новый адрес. После остановки контейнера временные сессии могут исчезнуть: это намеренное свойство учебного стенда, а не обещание постоянного хранилища.
Официальные источники: Docker Desktop в Windows.
Linux Bash: локальная практика и проверка HTTP
Нужны Docker Engine и современный Compose plugin. Для свежей Ubuntu подробная установка приведена ниже. Если вашему пользователю не выдан доступ к Docker, выполняйте docker-команды через sudo. На удалённой машине локальный порт можно открыть через SSH-туннель, сохранив привязку к loopback. Для публикации на домене используйте отдельный серверный вариант, который закрывает демонстрацию паролем.
mkdir -p ~/projects/htmx-lab
cd ~/projects/htmx-lab
curl -fL https://ae-nekrasov.ru/wp-content/plugins/aen-htmx-workshop/htmx-project.zip -o htmx-project.zip
unzip -n htmx-project.zip
cd htmx-project
docker compose config --quiet
docker compose up -d --build --wait
curl --fail http://127.0.0.1:8101/health.php
python3 scripts/http-smoke.py
Smoke-скрипт работает только с loopback-адресом и создаёт собственные учебные сессии. Он проверяет серверные маршруты, изменения данных, ошибки, изоляцию и ограничения. Проверка мобильной вёрстки и реального поведения кнопок выполняется отдельно в браузере: успешный HTTP-код сам по себе не доказывает удобство интерфейса.
Пример 1. Живой поиск с короткой задержкой
Вводить запрос удобнее, когда приложение не отправляет обращение на каждую букву. В первом примере событие input запускает отсчёт 300 мс; следующий ввод начинает ожидание заново. После паузы PHP получает q и возвращает до пяти карточек вместе с общим числом совпадений. Пустой поиск показывает начальную выборку, отсутствие результатов объясняется текстом.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<input name="q" hx-get="api.php?route=search"
hx-trigger="input changed delay:300ms"
hx-target="#search-results" hx-sync="this:replace">
<div id="search-results"></div>
Так можно искать клиентов, товары, документы или заявки. Сервер должен ограничивать длину запроса и размер ответа, а при работе с БД — использовать подходящий индекс и безопасные параметры SQL. В учебном наборе поиск выполняется по небольшому массиву: это позволяет видеть механику интерфейса, не выдавая демонстрационный алгоритм за готовый поиск по миллионам записей.
Индикатор появляется на время запроса. Искусственная задержка ответа здесь всего 220 мс, чтобы движение было заметно; блокировка PHP-сессии снимается до ожидания. Сохранение формы в соседнем примере не должно ждать, пока учебный поиск спит. В рабочем приложении задержку удаляют, а производительность измеряют на реальной нагрузке.
Официальные источники: hx-trigger, hx-indicator, Освобождение блокировки сессии.
Отмена запросов и защита от устаревшего ответа
Одна задержка ввода не решает все проблемы. Пользователь успел отправить поиск «сайт», затем заменил его на «отчёт», а первый ответ пришёл позже второго. Если безусловно вставлять каждый ответ, интерфейс покажет результат для старого текста. hx-sync=”this:replace” согласует запросы элемента: новая операция заменяет предыдущую. В мастерской также есть явная кнопка отмены отправленного поиска.
Отмена в браузере не является откатом серверной операции. Если PHP уже сохранил заявку, прерванное соединение не должно трактоваться как отмена сохранения. Для записи нужны понятные результаты, идемпотентность там, где она необходима, и возможность проверить состояние после потери ответа. Поэтому стратегию replace удобно применять к чтению, а сохранение формы дополнительно защищать от случайного повторного нажатия.
Отмена текущего запроса элемента
htmx.trigger(document.querySelector('#live-search'), 'htmx:abort');
Официальные источники: hx-sync, События htmx 4.
Пример 2. Фильтры и серверная пагинация
Во втором примере статус становится параметром запроса. PHP фильтрует общий набор, вычисляет число страниц и показывает по четыре записи. Кнопки перехода содержат текущий фильтр, поэтому на следующей странице человек продолжает работать с тем же списком. Недоступное направление перехода отключено; сервер ограничивает номер страницы допустимым диапазоном.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<form hx-get="api.php?route=board" hx-trigger="change"
hx-target="#board-results" hx-swap="outerHTML">
<select name="status">...</select>
</form>
Для административной панели важно различать состояние фильтра и состояние результата. После изменения фильтра обычно возвращаются на первую страницу: прежняя последняя страница может больше не существовать. Если список должен иметь адрес, которым можно поделиться, добавляют query-параметры страницы и механизм истории. В этой встроенной мастерской история намеренно отключена, чтобы учебные переключения не меняли навигацию основной статьи.
Официальные источники: hx-get, Адрес страницы и история.
Пример 3. Редактирование карточки на месте
Кнопка редактирования запрашивает HTML формы вместо карточки. После сохранения PHP возвращает обновлённую карточку с тем же ID контейнера. Если название слишком короткое, сервер сохраняет введённый текст и показывает объяснение. Отмена возвращает просмотр без изменения данных. В этой задаче outerHTML особенно нагляден: меняется целый компонент, а не произвольный кусок большой страницы.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<form hx-post="api.php?route=edit"
hx-target="#edit-card" hx-swap="outerHTML">
<input name="title" value="...">
<button>Сохранить</button>
</form>
Такой интерфейс подходит для коротких правок: название, приоритет, исполнитель, заметка. Для длинного документа отдельная страница может быть удобнее. В многопользовательской системе к этому сценарию добавляют проверку прав и версии записи: два сотрудника не должны незаметно перезаписывать изменения друг друга. htmx отвечает за запрос и отображение; конфликт версий должен обнаружить ваш backend.
Официальные источники: hx-swap, hx-disable.
Пример 4. Серверная валидация с HTTP 422
Форма создания специально позволяет отправить некорректные поля, чтобы показать серверную проверку. PHP проверяет длину названия и формат учебного email. При ошибке приходит HTTP 422 и новая форма: значения сохранены, проблемные поля имеют aria-invalid и связанные пояснения. В htmx 4 такой HTML может обновлять интерфейс без дополнительного обходного обработчика, привычного для старых примеров 2.x.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<form hx-post="api.php?route=create"
hx-target="#create-form" hx-swap="outerHTML" novalidate>
<!-- PHP возвращает поля и ошибки с HTTP 422 -->
</form>
Правильная проверка полезна человеку, когда объясняет способ исправления. Сообщение «ошибка» не говорит, что делать; «название от 5 до 60 символов» задаёт понятное правило. После успешного создания появляется номер заявки и обновляется общий счётчик. Email не сохраняется: здесь он нужен только для упражнения в проверке, а реальной отправки письма нет.
В обычном продукте встроенную HTML-валидацию оставляют как быстрый первый уровень, а PHP повторяет важные правила независимо от браузера. Пользователь может изменить HTML, отправить запрос вручную или попасть в устаревшую форму. Серверный контракт должен оставаться правильным в каждом из этих случаев. При превышении учебного лимита новых заявок возвращается отдельное сообщение и возможность сбросить свою копию.
Официальные источники: Обработка HTTP-статусов, Проверка значений в PHP.
Пример 5. Зависимые списки и проверка пары значений
Пользователь выбирает направление: веб-разработка, данные или поддержка. PHP возвращает подходящий список задач. Затем форма отправляет направление и выбранную задачу для учебного расчёта срока. Это привычная схема для выбора города и пункта выдачи, категории и модели, подразделения и сотрудника.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<select name="team" hx-get="api.php?route=specialists"
hx-target="#assignee-field" hx-swap="outerHTML"
hx-sync="this:replace">...</select>
Новый список помогает выбрать корректное значение, но не заменяет серверную проверку. Пока ответ загружался, человек мог изменить родительское поле; старое значение могло остаться в другом клиенте. Поэтому маршрут расчёта проверяет именно допустимую пару. Если сочетание неверно, он возвращает 422 и просит выбрать задачу заново. Число дней здесь фиксированное учебное правило, а не оценка реального проекта.
Официальные источники: Дополнительные поля запроса, hx-sync.
Пример 6. Кнопка «Показать ещё» без дублирования списка
Лента отдаёт по четыре события. Ответ заменяет только кнопку продолжения: на её месте появляются новые строки и следующая кнопка. Уже загруженные записи остаются в DOM. Последняя порция приходит без кнопки и сообщает, что список закончился. Это легко проверить в инспекторе: сервер возвращает сразу разметку событий и управление следующим шагом.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<button hx-get="api.php?route=feed&offset=4"
hx-target="this" hx-swap="outerHTML">Показать ещё</button>
Постепенная загрузка удобна для журналов, комментариев и истории действий. Для очень длинной ленты следует ограничивать количество элементов в памяти или использовать другую навигацию. В изменяющемся рабочем журнале смещение offset может давать пропуски и повторы при вставке новых событий; там обычно нужен устойчивый курсор. В нашей короткой учебной ленте порядок ограничен и понятен, поэтому используется простое смещение.
Официальные источники: hx-swap.
Пример 7. Архивация, отмена и обновление счётчика
Архивация меняет состояние заявки #2 только в вашей сессии. Она исчезает из активного поиска и списка, но остаётся доступной для восстановления в том же примере. Кнопка «Вернуть из архива» возвращает её обратно. Такая модель показывает полезную альтернативу немедленному удалению: человек видит результат действия и может его исправить.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<form hx-post="api.php?route=archive"
hx-target="#archive-card" hx-swap="outerHTML">...</form>
<!-- Дополнительный ответ обновляет общий счётчик -->
<span id="session-count" hx-swap-oob="innerHTML">23</span>
Один серверный ответ обновляет карточку и общий счётчик. Для дополнительной области используется out-of-band-фрагмент с hx-swap-oob. Это позволяет держать связанные показатели согласованными без второго ручного запроса. При этом источник истины остаётся на сервере: клиент не вычисляет новое число наугад и не уменьшает его повторно после случайного двойного нажатия.
Официальные источники: Обновление нескольких областей, hx-post.
Пример 8. Конечный опрос прогресса
Запуск сохраняет время начала учебной задачи в сессии. PHP вычисляет процент по прошедшему времени, а htmx получает новый фрагмент примерно раз в секунду. Пока работа условно продолжается, ответ содержит load delay:1s и заменяет сам себя. Когда достигаются 100%, следующий триггер отсутствует и запросы прекращаются. Опрос можно остановить отдельной кнопкой.
Ключевая разметка сценария; полный обработчик входит в исходники
<!-- Схема разметки; CSRF добавляет общий обработчик htmx:config:request. -->
<div id="job-progress" hx-get="api.php?route=progress"
hx-trigger="load delay:1s" hx-swap="outerHTML">...</div>
<!-- Готовый ответ больше не содержит hx-trigger -->
Важно понимать границу примера: шестисекундный таймер не создаёт файл и не выполняет тяжёлую обработку. В настоящем сервисе задача отправляется в очередь или отдельный процесс, а этот маршрут читает сохранённый статус. Для такой серверной части пригодится практика очередей BullMQ, если ваш backend использует Node.js; сам интерфейс может оставаться на HTML и PHP.
Не оставляйте опрос бесконечным без причины. Определите завершение, ошибку, отмену и допустимую частоту. Если пользователь ушёл с экрана, клиентский опрос должен прекращаться. Для интенсивных обновлений изучают SSE или WebSocket, но короткий редкий polling часто проще внедрить и диагностировать.
Официальные источники: hx-trigger.
Инспектор запросов и собственный JavaScript
htmx не запрещает JavaScript. В мастерской небольшой скрипт добавляет CSRF-заголовок, отмечает активный пример, копирует код и показывает журнал запросов. Событие htmx:after:request предоставляет ответ и контекст; inspector берёт HTTP-статус, время и текст HTML. Этот текст выводится через textContent, поэтому просмотр кода не выполняет его повторно.
Событие четвёртой ветки и один заголовок для изменяющих запросов
document.addEventListener('htmx:config:request', event => {
const ctx = event.detail.ctx;
if (ctx.request.method !== 'GET') {
ctx.request.headers['X-CSRF-Token'] =
document.querySelector('meta[name="csrf-token"]').content;
}
});
Дополнительный скрипт полезен там, где он делает интерфейс понятнее, а не скрывает всё поведение атрибутов. Например, после подмены большого блока можно перевести фокус в заголовок результата, а после ошибки — в первое неверное поле. Если подключается сторонний редактор или виджет, его инициализацию нужно согласовать с жизненным циклом новых DOM-элементов и освобождать ресурсы при удалении.
Официальные источники: События htmx 4.
Статусы, таймауты и понятное восстановление после ошибки
Ошибки нужно проектировать так же, как успешное сохранение. В мастерской 400 означает некорректный параметр, 403 — неверный токен или источник запроса, 405 — неподходящий метод, 413 — слишком большой запрос, 422 — исправимые поля, 429 — слишком частые обращения. Неизвестный маршрут отвечает 404. Пользователь получает текст действия, а не необработанный stack trace.
На запрос установлен таймаут восемь секунд. Если сеть пропала, интерфейс объясняет, что ответа нет, и сохраняет поля. Успешный серверный код не следует смешивать с успешной доставкой ответа: запись могла сохраниться до обрыва соединения. Для финансовых и других значимых операций нужен ключ идемпотентности и отдельное чтение результата; повторное нажатие не должно дублировать действие.
В htmx 4 ответы ошибок могут вставляться в target. Поэтому backend должен возвращать ожидаемый безопасный фрагмент и в неуспешном случае. Если перед приложением стоит прокси со своей HTML-страницей ошибки, предусмотрите обработку такого ответа. Полезно различать ошибку конкретного поля, истёкшую сессию и временную недоступность всей системы.
Официальные источники: Обработка HTTP-статусов, События htmx 4.
HTML, CSRF и границы учебной сессии
Разметка с сервера должна быть безопасна независимо от библиотеки. Пользовательское название перед выводом проходит htmlspecialchars с экранированием кавычек и UTF-8. В результате текст вроде <b> остаётся текстом заявки. Нельзя вставлять непроверенный ввод непосредственно в HTML, атрибуты, URL или JavaScript: каждый контекст требует собственного правила обработки.
function e($value): string {
return htmlspecialchars((string)$value,
ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
echo '<h4>' . e($item['title']) . '</h4>';
Изменяющие маршруты принимают POST и проверяют случайный CSRF-токен с hash_equals. Cookie имеет отдельное имя, узкий путь, HttpOnly и SameSite=Lax; при HTTPS включается Secure. Демо не подключает WordPress, не читает его пользователей и не пишет в общую базу. Файлы сессий находятся в отдельном приватном каталоге системного temp; очистка не затрагивает сессии других приложений. У каждого посетителя свой небольшой набор, а ограничения применяются на сервере, а не только в форме.
Такая сессия нужна для изоляции упражнения, а не для настоящей авторизации. В рабочем приложении дополнительно проверяют, кто вошёл и какие записи ему принадлежат. Ограничение частоты на сессию также не заменяет инфраструктурную защиту: злоумышленник способен создавать новые сессии. Общие лимиты и наблюдение относятся к размещению сервиса и его реальной нагрузке.
Официальные источники: Экранирование HTML в PHP, Сессии PHP, Сравнение токенов.
Доступность, фокус и поведение без JavaScript
Динамическое обновление удобно, когда человек понимает, что произошло. Поля имеют подписи, ошибки связаны с полями, кнопки достаточно крупные, область результата объявляет изменения через aria-live. Для долгого запроса нужен видимый индикатор; для окончательного результата — ясное сообщение. На узком экране карточки перестраиваются, а код прокручивается внутри своего блока.
Полная замена элемента может удалить текущий фокус, выделение или состояние вложенного виджета. Выбирайте минимальную область обновления и проверяйте клавиатуру. В сложном редакторе может помочь morphing, который старается сохранить существующие элементы. Он не отменяет необходимости понимать ID, состояние формы и жизненный цикл подключённых компонентов.
Эта мастерская требует JavaScript, поскольку показывает htmx и инспектор. В рабочем сайте можно начать с обычных ссылок и форм, добавить action, method и полноценный серверный ответ, а затем улучшить их через hx-boost. Это отдельное проектное решение: наличие нескольких hx-атрибутов само по себе не гарантирует корректную работу без JavaScript.
Официальные источники: Обновление DOM с сохранением элементов, Улучшение обычных ссылок и форм.
Когда htmx удобнее, а когда нужен другой инструмент
Для серверных форм, каталога и внутренней панели htmx помогает сохранить одну основную модель шаблонов. Она хорошо сочетается с PHP, где HTML естественно формируется на сервере. Часть локальных переключений можно делать обычным JavaScript или Alpine.js, если эта связка соответствует проекту.
Если приложение много работает офлайн, содержит сложный графический редактор, множество зависимых клиентских состояний или должно мгновенно выполнять десятки локальных преобразований, отдельный клиентский фреймворк может быть удобнее. Выбор определяется задачей и командой. htmx не устраняет задержку сети, нагрузку backend и необходимость поддерживать маршруты; она предлагает другой способ организовать динамический интерфейс.
Ubuntu 24.04 VPS: установка и размещение проекта
Для отдельного сервера в архиве есть полный deploy/compose.server.yaml. Он публикует только Caddy на 80/443, а PHP остаётся во внутренней сети. Учебная мастерская закрывается общим паролем поверх HTTPS. Подготовьте свежую Ubuntu 24.04, SSH с sudo и домен. A-запись должна указывать на сервер; AAAA используйте только при работающем IPv6. Во внешнем firewall разрешите TCP 80/443 и свой административный SSH-доступ.
Первичная установка на новой Ubuntu 24.04
sudo apt-get update
sudo apt-get install -y ca-certificates curl unzip python3
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
arch=$(dpkg --print-architecture)
printf 'Types: deb\nURIs: https://download.docker.com/linux/ubuntu\nSuites: noble\nComponents: stable\nArchitectures: %s\nSigned-By: /etc/apt/keyrings/docker.asc\n' "$arch" | sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo docker compose version
Если Docker уже обслуживает проекты, сохраните существующую установку и используйте официальную процедуру обновления. Проверьте занятые порты через sudo ss -lntp: работающий прокси нельзя просто заменить учебным контейнером. Публикация портов Docker также требует понимания правил firewall, включая особенности UFW. Для первого опыта проще отдельный VPS с понятной конфигурацией.
sudo install -d -m 0755 /opt/aen-htmx/releases/v1
sudo curl -fL https://ae-nekrasov.ru/wp-content/plugins/aen-htmx-workshop/htmx-project.zip -o /opt/aen-htmx/releases/v1/source.zip
sudo unzip /opt/aen-htmx/releases/v1/source.zip -d /opt/aen-htmx/releases/v1
sudo ln -s /opt/aen-htmx/releases/v1/htmx-project /opt/aen-htmx/current
cd /opt/aen-htmx/current
Официальные источники: Docker Engine на Ubuntu, Compose на сервере.
Домен, пароль и запуск приватной мастерской
Секретная конфигурация хранится отдельно от исходников. Создайте закрытый каталог в /etc, скопируйте шаблон один раз и задайте SITE_DOMAIN без схемы и пути. Команда hash-password спросит пароль интерактивно. Полученный bcrypt-хеш вставьте в BASIC_AUTH_HASH в одинарных кавычках, чтобы Compose сохранил символы $. Имя для входа в примере — reader.
sudo install -d -m 0700 /etc/aen-htmx
sudo install -m 0600 deploy/site.env.example /etc/aen-htmx/site.env
sudo docker run --rm -it caddy:2.11.9-alpine caddy hash-password
sudoedit /etc/aen-htmx/site.env
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml config --quiet
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml build web
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml run --rm --no-deps caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml up -d --no-build --wait
curl -I https://demo.your-domain.tld/
curl --fail --user reader https://demo.your-domain.tld/health.php
Без пароля ожидается 401, с паролем — JSON status: ok. В браузере откройте домен, войдите и проверьте все интерфейсы. Caddy получает и продлевает сертификат при выполнении условий DNS и сетевой доступности; его хранилище сохраняется в named volume. Healthy-контейнер проверяет внутренний ответ и сам по себе не доказывает успешный публичный TLS. Внешний домен нужно проверить отдельно из другой сети.
Basic auth ограничивает вход в демонстрацию, но не создаёт личные аккаунты внутри приложения. Серверный вариант подходит для закрытого учебного инструмента. Для использования с реальными клиентами нужно заменить временные данные постоянным хранилищем и добавить полноценную модель доступа. Не публикуйте заполненный env и не выводите полный docker compose config в общий лог.
Официальные источники: HTTPS в Caddy, Закрытый вход в Caddy.
Журналы, обновление и возврат к прежней версии
Docker запускается через systemd, контейнеры имеют restart: unless-stopped, журналы ограничены тремя файлами по 10 MB. Во время диагностики смотрите PHP и Caddy вместе: один показывает ошибку приложения, другой — входящий запрос. Изменение Caddyfile сначала проверяется validate, затем применяется reload. Если изменён env, нужен обычный up, чтобы пересоздать контейнер с новым окружением.
cd /opt/aen-htmx/current
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml logs --tail 100 web caddy
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml exec -T caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo docker compose --env-file /etc/aen-htmx/site.env -f deploy/compose.server.yaml exec -T caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
Новый код размещайте в releases/v2/htmx-project. Сохраните прежний env, установите APP_RELEASE=v2, соберите образ web и выполните up из нового каталога. Проверьте health, форму и поиск, затем переключите current. Для отката верните прежний env и каталог v1, выполните up -d –no-build –wait: старый тег образа должен оставаться доступным. Точные команды есть в deploy/README.md; они не удаляют предыдущий релиз.
Официальные источники: Автоматический запуск контейнеров.
Что сохранять и как восстанавливать такой стенд
Учебные записи находятся в PHP-сессиях. В Docker каталог сессий размещён в ограниченном tmpfs и может очищаться при остановке; это не база заявок. Пользователь может скачать свою синтетическую копию JSON, но экспорт не является механизмом восстановления приложения. Если нужна долговременная работа, проект расширяют БД, миграциями и отдельной процедурой резервирования бизнес-данных.
Для текущего серверного варианта сохраняют исходники релиза, закрытый env и тома Caddy с сертификатами. В архиве есть backup/restore-инструмент: он кратковременно останавливает Caddy для согласованного копирования, проверяет контрольные суммы и восстанавливает только в новые тома. Приватные ключи остаются в закрытом backup, не попадают в ZIP исходников и не размещаются в public. Полная инструкция описывает переключение и откат, а локальная проверка не объявляется выполненным выпуском сертификата на чужом VPS.
Исходники, границы проверки и следующий шаг
В архив входят PHP-маршруты, шаблоны, восемь интерфейсов, библиотека htmx 4.0.1, Bootstrap, Dockerfile, отдельный серверный Compose, инструкции Windows/Linux и лицензии. HTTP smoke проверяет настоящий PHP: чтение, запись, валидацию, архивацию, завершение опроса, CSRF, изоляцию сессий и ограничения. Результаты записаны в evidence отдельно от проверки браузера.
Используйте проект как рабочий учебный пример и основу для небольшого интерфейса. Выберите один сценарий, перенесите его маршрут и шаблон к своей модели данных, добавьте проверку прав, затем повторите успешный и ошибочный путь. Такой порядок позволяет понять каждый переход от действия пользователя к серверному результату и постепенно построить интерфейс, который удобно поддерживать.
Открыть мастерскую на весь экран ↗Скачать PHP-проект и исходники ↓
Официальные источники: Документация htmx 4, Релиз htmx 4.0.1.


