Creating Pages and htmx Routes
In DeploySolo, a “page” is usually a normal GET route that renders a full template (e.g., todo.html). An htmx route is a route that returns partial HTML (often a single template block) to swap into the page without a full reload.
1) Register a page group and routes
Create a package in examples/<yourapp>/app/ (like todo.go) and register routes from your OnServe() hook via your app’s RegisterPages(se) (see main.go).
A typical pattern:
- Group your feature routes (e.g.,
/tasks) - Bind auth middleware once on the group
- Add:
GET ""for the full pagePOST/PUT/DELETEfor htmx actionsGET "/{id}"to render an “edit” partial
From the Todo example:
- Full page:
GET /tasks→renderTasksPage - htmx create:
POST /tasks→ returns onetaskblock - htmx edit form:
GET /tasks/{id}→ returns oneupdatetaskblock - htmx update:
PUT /tasks/{id}→ returns one updatedtaskblock - htmx delete:
DELETE /tasks/{id}→ returns empty HTML to remove the row
2) Render full pages vs partial blocks
Use the render package in two modes:
- Full page render:
render.Render("todo.html", data)
This should return a complete HTML document (your<!DOCTYPE html>template). - Partial render:
render.RenderRaw("task", task)
This should return only a named template block (no<html>, no<body>).
This keeps your pages clean and makes htmx swaps predictable.
3) Use htmx attributes to swap the right fragment
In templates:
- For create:
hx-post="/tasks" hx-target="#tasks" hx-swap="afterbegin" - For edit:
hx-get="/tasks/{{ .ID }}" hx-target="#task-{{ .ID }}" - For update: form
hx-put="/tasks/{{ .ID }}" hx-target="#task-{{ .ID }}" - For delete:
hx-delete="/tasks/{{ .ID }}" hx-target="#task-{{ .ID }}" hx-swap="outerHTML"
Key rule: Your htmx handler must return HTML that matches the target element’s shape. If you target #task-<id>, return a single root element with id="task-<id>" so outerHTML swaps cleanly.
Tip: If you see layout “drift” after swaps, check for mismatched wrapper elements or missing classes between the view block and update block.