htmdart 0.7.2
htmdart: ^0.7.2 copied to clipboard
Build simple systems with Dart + HTMX

Build fast, hypermedia-driven web apps with Dart + htmx
Index #
Introduction #
Htmdart was born out of a simple need: to move fast when building hypermedia-driven web applications in Dart. While frameworks like shelf (or newer ones like relic) provide excellent foundations, they often lack certain conveniences that make everyday development smooth.
That’s why Htmdart gives you:
- Direct HTML rendering in Dart - powered by the separate htmleez package, so you can compose markup anywhere.
- Attributes for htmx & hyperscript - all the
hx-*and_attributes available as htmleez attributes. - A router built for speed & DX - radix-tree based, with grouping, middlewares, static file serving, and redirects included out of the box.
- A custom
servefunction - wrappingshelf_io.serve, with extras like hot-reload and opinionated defaults.
These tools are not meant to reinvent the Dart web ecosystem. They exist because, today, the existing solutions aren’t yet at the level where building a complete hypermedia app feels frictionless. Htmdart fills that gap.
Looking Ahead #
The long-term vision of Htmdart is actually to become smaller, not bigger.
- The essential piece is attribute helpers (htmx, hyperscript, etc.) - those are stable and unlikely to change.
- Routers, serve utilities, or other abstractions may be deprecated in the future, once Dart’s web frameworks mature enough to cover these features well.
In short: Htmdart is a bridge - giving you the speed and ergonomics you need right now, while leaving room for you to adopt other frameworks as they evolve.
Hello World Example #
import "dart:io";
import "dart:math";
import 'package:htmdart/htmdart.dart';
final router = Router()
..get("/", homePage)
..get("/random", randomNumber);
void main() => serve(router, InternetAddress.anyIPv4, 8080, withHotreload: false);
Response homePage(Request _) => respondWithHtml([
html([
body([
div([$id("number"), "0".t]),
button([
$hx.get("/random"),
"Get Random Number".t,
]),
]),
]),
]);
Response randomNumber(Request _) => respondWithHtmlOob([
div([
$id("number"),
Random().nextInt(100).toString().t,
]),
]);
Core Concepts #
1 HTMX utilities #
The hx class provides all the standard HTMX attributes
- Simple verbs
hx.get("/path") hx.post("/submit") hx.put(...) hx.delete(...) - Dynamic handler binding
// Let's imagine this handler Response handleNoteDetails(Request req, String noteId) {...} final router = Router()..get("/notes/<noteId>", handleNoteDetails); // Then in your elements you can automatically pick up the HTTP verb and route $hx.handle(handleNoteDetails) // In case your route has path parameters (like in this case) // you can pass them in order as a List<String> $hx.handler(handleNoteDetails, [note.id]) // You can also pass query parameters $hx.handler(handleNoteDetails, [note.id], {"fromQP": true}) // It renders to // hx-get="/notes/myNoteId?fromQP=true"; - Swap controls
$hx.swap.innerHTML $hx.swapOob.yes // out-of-band swaps - Extras
All the available hx attributes from htmx have been added, you can see them here$hx.vals("js:{ count: … }") // inject JSON values $hx.select(".result") // response selector $hx.confirm("Are you sure?")
2 Hyperscript attribute #
Built-in support for hyperscript's attribute
div([
className("btn"),
$_("on click add .active to me then wait 500ms then remove .active"),
"Click me".t,
])
3 Router #
final router = Router()
..notFoundHandler((req) => html([h1(["404".t])]).response)
..get("/", homePageHandler)
..post("/increment", incrementHandler);
void main() async {
final server = await io.serve(router.call, 'localhost', 8080);
print("Listening on ${server.port}");
}
- Path parameters
Response handler(Request req, String param) {...} final router = Router()..get("/handler/<param>", handler); - Any Method
Response handler(Request req, String param) { switch(req.method) { case "GET": ... case "POST": ... } } final router = Router()..any("/any", handler); - Grouping
final apiGroup = router.group("/api"); apiGroup.get("/items", listItems); apiGroup.post("/items", createItem); - Static files
router.static("/public", "web/public"); - Middleware
- Built-in support for middlewares with
router.use(myMiddleware)
- Built-in support for middlewares with
- Redirects
Redirect not defined routes with
router.redirect("/", "/login"),
4. Helpers #
-
Response shortcuts
respondWithHtml([...]): wraps a list ofHTMLfragments into a fullResponsewith the correctContent-Type.respondWithHtmlOob([...]): same as above, but optimized for out-of-band swaps in htmx (adds$hx.swapOob.yesto all the fragments).
-
Request extensions
On anyRequestyou can check htmx metadata:if (request.isHx) { ... } // true if HX-Request == "true" print(request.hxTarget); // target element print(request.hxTriggerName); // trigger name