Conduit
Conduit is how you build interactive UI in Almasix without leaving Python.
Write a component class. Render a Prism template. Sprinkle wire:* attributes in the HTML. Conduit keeps server state and the browser in sync — clicks, forms, typing, the whole dance — over a signed, CSRF-safe wire protocol. Pair it with Alpine.js when you want a little client-side sparkle, and $wire bridges the two worlds.
The idea in thirty seconds
Section titled “The idea in thirty seconds”from almasix.conduit import Component
class Counter(Component): count = 0
def increment(self) -> None: self.count += 1
def render(self) -> str: return "conduit.counter"<div> <h1 conduit:text="count">{{ count }}</h1> <button type="button" conduit:click="increment">+</button></div>@conduitScripts<conduit:counter />Click the button. Conduit posts a tiny update, runs increment() on the server, and morphs the DOM. The wire:text binding updates instantly from local memo so it feels like JavaScript — even though the truth still lives in Python.
That’s the whole game: server is the source of truth, the browser stays snappy.
Why Conduit?
Section titled “Why Conduit?”Most “interactive” apps today split the world in two: a JSON API on one side, a frontend framework on the other, and a pile of glue in between. Conduit collapses that for the common case.
- One language for the feature — state, validation, and actions live in a Python class.
- HTML you already know —
wire:click,wire:model,wire:submit. No build step required to get started. - Optimistic bindings —
wire:text,wire:show, andwire:bind:*update from client memo before the morph lands. - Alpine when you want it —
$wirelets Alpine read and call into your component without a second state store. - Secure by default — CSRF plus signed update URLs (HMAC + expiry). Tampered snapshots get a hard no.
A taste of Alpine
Section titled “A taste of Alpine”Conduit ships Alpine alongside its script (or you bundle your own). Inside a component, $wire is your component:
<div x-data="{ hovering: false }" @mouseenter="hovering = true" @mouseleave="hovering = false"> <p> Count is <span x-text="$conduit.count"></span> <span x-show="hovering" x-transition>(hover tip from Alpine)</span> </p> <button type="button" @click="$conduit.increment()">+</button> <button type="button" @click="$conduit.count = 0">Reset locally + sync</button></div>Local Alpine state (hovering) stays in the browser. Server state (count) stays on $wire. They play nicely together — that’s the point.
Package
Section titled “Package”Install almasix-conduit (or the almasix[conduit] extra). The import path is always:
from almasix.conduit import Component, Conduit, conduitThe service provider registers itself via the almasix.providers entry point. Drop @conduitScripts in your layout, embed with <conduit:name /> or @conduit('name'), and you’re off.
- Installation — install, publish config & assets
- Quick start — counter end-to-end, and why it feels fast
- Embedding —
<conduit:…>,@conduit, dualconduit:/wire:vocabulary - Components — state, lifecycle, actions, validation, nesting, lazy
- Full-page components —
Route.conduit, layouts, params, query strings - Directives — the full
conduit:/wire:vocabulary with examples - Alpine and islands —
$conduit/$wire,$errors,$entangle, scoped morph - Wire protocol — what goes over the wire
- Configuration — signed URLs, subpaths, knobs
- Features — what’s in the box