1 unstable release
Uses new Rust 2024
| 0.1.0-alpha.1 | Dec 18, 2025 |
|---|
#1177 in Template engine
Used in 3 crates
9KB
107 lines
loomx-core
Framework-agnostic core for loomx.
This crate contains the component registry, rendering entrypoints, props parsing helpers, and validation utilities. It intentionally has no HTTP framework dependencies.
Most users will depend on the umbrella crate loomx instead.
loomx
loomx is a strongly‑typed, server‑driven UI framework for Rust. It renders HTML fragments and pages using typed components, designed for HTMX‑first applications with minimal JavaScript.
At its core, loomx lets you:
- Define components as typed Rust structs
- Render them using Askama templates
- Register components at compile time
- Render fragments dynamically by name
- Integrate cleanly with Axum
- Avoid JavaScript-heavy frontend frameworks
Project Goals
- Strong typing end‑to‑end (props → templates → rendering)
- Server‑driven HTML with HTMX
- Explicit, debuggable architecture
- Framework‑agnostic core
- Thin transport integrations (Axum today, others possible later)
Non‑goals:
- Client‑side virtual DOM
- JS component systems
- Implicit magic routing
Workspace Layout
loomx/
├─ crates/
│ ├─ loomx-core # Registry, rendering, props parsing
│ ├─ loomx-macros # register_component! macro
│ ├─ loomx-axum # Axum integration (routes, handlers)
│ └─ loomx-cli # CLI (experimental)
├─ components/
│ └─ loomx-components-basic
│ └─ Example component pack
├─ examples/
│ └─ basic # Example Axum app
Core Concepts
Components
A component is:
- A Rust struct (typed props)
- An Askama template
- A stable string name
#[derive(Template, Deserialize)]
#[template(path = "components/hello.html")]
pub struct Hello {
pub name: String,
}
impl Component for Hello {
const NAME: &'static str = "hello";
}
register_component!(Hello);
Component Registration
Components are registered at compile time using inventory.
- No global mutable registries
- No runtime plugin loading
- Deterministic startup behavior
Duplicate component names are detected at startup.
Rendering
use loomx::render;
let html = render("hello", json!({ "name": "Ada" }))?;
Rendering returns HTML or a typed error.
Props Parsing (Core)
loomx-core provides a transport‑agnostic parser:
application/jsonapplication/x-www-form-urlencoded
parse_props(content_type, body_bytes)
This logic is reused by all integrations.
Axum Integration
loomx-axum provides:
Generic Fragment Routes
GET /fragments/:name→ query paramsPOST /fragments/:name→ form or JSON body
Router::new().merge(loomx_axum::fragment_router())
Typed Fragment Routes (Ergonomic)
Router::new().merge(
loomx_axum::typed_fragment_routes! {
"/fragments/hello" => ("hello", HelloProps),
}
)
This generates:
GET /fragments/helloPOST /fragments/hello(form)POST /fragments/hello/json(JSON)
All fully typed.
HTMX Support
loomx is HTMX‑first:
- Fragment responses are HTML
- HTMX headers are detected
- Designed for
hx-get,hx-post,hx-swap
No JS framework required.
Logging
Structured logging via tracing:
- Component render attempts
- HTMX detection
- Request metadata
- Startup validation
Safety & Correctness
- Strong typing everywhere
- Duplicate component detection
- Explicit linking of component crates
- No hidden runtime state
Status
loomx is early but functional.
Current focus:
- API stabilization
- Documentation
- CLI & tooling
- More integrations (Actix / Poem)
License
MIT
Update: Typed Rendering & Gallery
loomx now provides an ergonomic typed rendering helper:
render_with<T: Serialize>(name, &T)— preferred for application code
This avoids manual construction of serde_json::Value while preserving the dynamic
component registry model.
The example application includes an interactive component gallery at /gallery
showing:
- inline rendering
- HTMX-powered preview slots
- typed fragment routes under
/typed/...
See docs/gallery.md for details.
Dependencies
~7–10MB
~128K SLoC