1 unstable release

Uses new Rust 2024

0.1.0-alpha.1 Dec 18, 2025

#1177 in Template engine


Used in 3 crates

MIT license

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/json
  • application/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 params
  • POST /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/hello
  • POST /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


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