Skip to content

situ

situ situ

Site your state, derive the wire.

situ lets a Python developer write a reactive web UI as one component (real HTML on top, Python signals and handlers below) and declare, per piece of state, where it lives. A real Python→JS compiler reads that declaration and emits a small, app-specific client island; the client/server boundary stays explicit and is enforced at compile time. You author no client JavaScript and can read every line the compiler ships.

situ's compiler and mount core are framework-neutral. It ships a Litestar adapter (the reference), a generic ASGI one for any ASGI host (FastAPI, Starlette, bare uvicorn) that imports no web framework at all, and a Flask one; a component mounts on any of them unchanged. It is built for Python full-stack developers who reach for hypermedia (htmx, Datastar) and hit the wall where a piece of state wants to live in the browser: a live search, a selection, an open dialog.

Alpha software

situ is a young, alpha library extracted from a research programme (the webui repository). The compiler accepts a bounded dialect of Python and fails closed on anything outside it. See Status & roadmap for the current limits.

The one idea: site each piece of state

Every signal declares its site. The transport follows from that, so you never write a fetch, a target, or an SSE wire.

Site Where it lives What the compiler derives
Local[T] the browser compiled to JS, zero network
Url[T] the query string a shareable link the server re-renders
Server[Facade] the database a POST command that re-renders one region
Synced[T] a local-first replica reserved, see Status

The boundary is a compile-time invariant: a client read of Server-sited state is a CompileError, so database-backed state cannot reach the browser by accident.

Sixty seconds of situ

A component is two sibling files sharing a stem:

from situ import Local

count: Local[int] = 0  # client state — compiled to JS, no network


def bump() -> None:
    global count       # names the local signal this handler writes
    count = count + 1
<div data-region>
  <button @click="bump">+1</button>
  <strong :text="count"></strong>
</div>

One mount_static_component(...) call serves it; the compiler emits the island. The Quickstart walks through the whole thing, and Server components adds the database seam.

The flagship demo: a master-detail issue tracker whose entire client is a single generated island

What makes it different

Four properties hold simultaneously, and each ships with its own falsifier:

  1. The seam is proven at build time. Client access to server state fails the build, whether a read, a write, or a loop (Sites & the seam). There is no code-generation path that could ship database I/O to the browser.
  2. What compiles means in the browser what it means in Python. The client dialect is specified as a table of typing rules, each verified against CPython, so ==, in, arithmetic, and truthiness behave the way the Python reads, with no "passes the test, breaks in the browser" surprise. A differential oracle drives the disagreement count to zero, and knowing the types lets the compiler accept more (k in d, xs[-1], 'ab' * 3, n % m all work).
  3. The wire is legible. In the unified idiom, a reactive loop declares exactly which per-row fields cross to the client; referencing anything else is a CompileError, so over-fetching is impossible by construction.
  4. Decomposition is free. Composition (props, events, slots, provide/inject, per-instance state, scoped CSS) resolves entirely at compile time into one island. However you slice a page into components, it emits the same program as the hand-written monolith, pinned by a property test over generated trees. No runtime layer, no extra generated code; only a private namespace where a component needs one.

The generated island for every shipped demo measures 5–12 KB unminified, over a ~700-line shared runtime shim that contains no expression interpreter.

What's in the box

  • situ: the Python→JS compiler (pure standard library) and a framework-neutral mount core, with a Litestar adapter (mount_component, the reference), a generic ASGI one (situ.mount.asgi.asgi_app), and a Flask one (situ.mount.flask.mount_flask). API reference.
  • situ_ui: a dense-enterprise component kit: 24 components, a CSS class contract, compiled interactions.
  • situ.declui: declui, a compile-time declarative-UI generator: a typed model + a small Screen become a working form, list, or master-detail tracker.
  • A gallery of demos and declui examples, each browser-verified. Run them.

Where next