Files
..

thymeleaf-htmx

Companion project for Spring Boot 4 + Thymeleaf + htmx: Server-Side CRUD UI in 2026 on ankurm.com.

A contacts list with search, pagination, create-with-validation, inline edit and delete. All HTML is rendered by Thymeleaf; htmx only decides which part of a page to replace. About 25 lines of JavaScript-free attributes, no JS framework, no build step.

Every console block in the article is quoted from output/. The MockMvc transcripts are written by the tests themselves (they assert on the same data), and output/11-browser-check.txt comes from headless Chromium driving the packaged jar.

Versions

Spring Boot 4.1.2 (latest GA on Maven Central; 4.2.0-RC1 exists but is not GA)
Thymeleaf 3.1.5.RELEASE (managed by Boot)
htmx 2.0.11 as the WebJar org.webjars.npm:htmx.org (htmx 4.0.0 exists on npm next; see the article)
JDK 25
Test tooling spring-boot-starter-webmvc-test, spring-boot-starter-thymeleaf-test; Playwright 1.64.1 for the browser check (optional)

Run

export JAVA_HOME=/path/to/jdk-25
mvn spring-boot:run          # http://localhost:8080/contacts
mvn test                     # 10 MockMvc tests, rewrite output/01..09
./scripts/run-all.sh         # tests + live curl + browser check, regenerates all of output/

The browser check needs Node 20+ and a one-time npx playwright install chromium.

Where things are

Concern File
Layout fragment (layout(title, content)), htmx config meta tag, WebJar script tag templates/layout/base.html
List page + the table fragment htmx receives templates/contacts/list.html
Row, edit-row (also the error state) templates/contacts/fragments.html
Create form (full page and form fragment) templates/contacts/new.html
Fragment vs full page decision (HX-Request, HX-History-Restore-Request) Htmx.java, ContactController.java
Vary header VaryHtmxFilter.java
Contract tests ContactUiTest.java, Transcript.java
Real-browser behaviour checks browser-check/check.mjs

Output

File Shows
output/00-mvn-test.txt surefire summary
output/01-full-page-vs-fragment.txt same URL, with and without HX-Request
output/02-history-restore-gets-full-page.txt the history-miss case
output/03-htmx-webjar.txt version-less WebJar path resolved to the pinned version
output/04-pagination-and-search.txt last page, page past the end, search
output/05-create-validation-errors.txt 422 with errors, fragment and full page
output/06-create-success.txt HX-Redirect vs 302
output/07-inline-edit.txt edit row, failed save, successful save
output/08-delete.txt 200 + HX-Trigger, 404
output/09-delete-last-page-clamps.txt pager after deleting the last page's rows
output/10-live-curl.txt real Tomcat answering with curl
output/11-browser-check.txt Chromium: swaps, history, 204 and 422 traps

Not covered: Spring Security / CSRF (no Security dependency in this module), file uploads, and htmx 4.