Files
spring-boot-demo/thymeleaf-htmx/README.md
T

61 lines
3.1 KiB
Markdown

# thymeleaf-htmx
Companion project for **Spring Boot 4 + Thymeleaf + htmx: Server-Side CRUD UI in 2026** on **[ankurm.com](https://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
```bash
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.