imaimai17468/hypersig
imaimai17468/hypersig has 48 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-09 18:04 UTC. Last push 2026-11-02.
Server-driven hypermedia for Rust where signal names, attribute expressions, actions, and patch targets are type-checked
Measured star growth
- in 24h
- 0
- in 7 days
- 0
- in 15 days
- Update delayed
- in 1 month
- Update delayed
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
README.md
Originalhypersig
Server-owned state, type-checked strings.
hypersig is a hypermedia web framework on axum in which the page, its behavior in the browser, and the server handlers are all written in Rust. The server holds the state and sends the browser patches. Every string the browser acts on (signal names, expressions, action URLs, links) is produced from a Rust type, so a mistake in one is a compile error.
cargo buildbuilds the whole application, with no Node or wasm step.- The first HTML already holds the page's state, so the page needs no hydration, and an action answers with only the values that changed (Why hypersig).
- A form's rules, CSS, and translations are checked by the compiler as well, and a component set styled after shadcn/ui comes with it (Forms, Styling, Translations, Components).
Quick start
Add the dependencies to Cargo.toml:
[dependencies]
hypersig = "0.15"
axum = "0.8"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
Write src/main.rs:
use axum::Router;
use hypersig::html::{button, div, span};
use hypersig::{Action, Page, Patches, Route, RouterExt, Signals};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
struct Counter {
count: i32,
}
hypersig::routes! {
fn pages(Router<()>);
const INDEX: Route = "/" => index;
action RESET: Action<Counter> = post "/reset" => reset;
}
async fn index((): (), (): ()) -> Page<Counter> {
let count = Counter::count();
Page::new("Counter", Counter { count: 0 }).child(
div()
.child(button().on_click(count.set(count + 1)).text("+1"))
.child(span().bind_text(count.to_text()))
.child(button().on_click(RESET.call()).text("Reset on the server")),
)
}
async fn reset((): (), (): ()) -> Patches<Counter> {
Counter::count().patch(0).into()
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let app = pages(Router::new()).hypersig_runtime();
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, app).await
}
Run cargo run and open http://127.0.0.1:3000. The +1 button counts in the browser without a request, and the reset button calls /reset, whose patch sets count back to 0. Misspelling count, or adding a string to it, stops cargo build. With the CLI (cargo install --locked hypersig_cli), cargo hypersig new my-app starts a project and cargo hypersig dev rebuilds and reloads it after each change, building with blaze, which it needs installed. The documentation continues from here.
Framework comparison
Why hypersig compares hypersig with Topcoat and Datastar, which come closest, in more detail.
| hypersig | Topcoat | React | Next.js | TanStack Start | Nuxt | Datastar | Leptos | Dioxus | |
|---|---|---|---|---|---|---|---|---|---|
| Page written in | Rust | Rust | TypeScript | TypeScript | TypeScript | TypeScript and Vue templates | HTML attributes | Rust | Rust |
| Server written in | Rust | Rust | Any | TypeScript | TypeScript | TypeScript | Any | Rust | Rust |
| No browser build | Yes | Yes | No (Vite) | No (Node bundler) | No (Vite) | No (Vite) | Yes | No (cargo leptos, to Wasm) |
No (dx, to Wasm) |
| Request types checked at compile time | Yes | Yes | No | Yes | Yes | No | No | Yes | Yes |
| Links checked against routes at compile time | Yes | Handler only (parameters at run time) | No (no router) | Opt-in (typedRoutes) |
Yes | Experimental (typedPages) |
No | No (href is a string) |
Yes (a Routable enum value) |
| State kept in the browser | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
Benchmarks
The test app is a 2000-item TodoMVC that stores every add, toggle, and delete on the server. Each number is the median of 10 runs in Chrome 155 on an Apple M4 Max, with the CPU slowed 4x and 50 ms added to every request, and the fastest and slowest of the 10 runs are in parentheses.
| hypersig | Topcoat | React | Next.js | TanStack Start | Nuxt | Datastar | Leptos | Dioxus | |
|---|---|---|---|---|---|---|---|---|---|
| JS (gzip) | 19.7 KB | 14.8 KB | 67.1 KB | 133.3 KB | 119.5 KB | 59.2 KB | 13.1 KB | 5.5 KB | 15.7 KB |
| Wasm (gzip) | - | - | - | - | - | - | - | 145.1 KB | 441.4 KB |
| First Contentful Paint | 239 ms (231–309) | 221 ms (206–241) | 209 ms (198–266) | 240 ms (229–261) | 237 ms (217–251) | 236 ms (223–263) | 217 ms (204–249) | 232 ms (222–240) | 213 ms (205–244) |
| Total Blocking Time | 3 ms (0–37) | 318 ms (311–351) | 16 ms (11–23) | 14 ms (9–16) | 0 ms (0–0) | 36 ms (29–42) | 20 ms (17–23) | 47 ms (42–57) | 122 ms (114–126) |
| Interaction to Next Paint | 24 ms (24–24) | 24 ms (24–24) | 24 ms (16–24) | 24 ms (16–24) | 24 ms (16–24) | 24 ms (24–24) | 16 ms (16–16) | 24 ms (16–24) | 72 ms (72–80) |
The apps and the harness are in bench/compare, measured at commit 7d031d0. Leptos and Dioxus load most of their code as WebAssembly, which the Wasm row counts. bench/js-framework-benchmark has the results of js-framework-benchmark, and the hypersig_style README compares the styling crates. On the same machine, examples/todomvc (485 lines) builds from an empty target directory in 13.4 s and rebuilds after a one-line change in 1.3 s (cargo build, Rust 1.99.0).
Deploying and security
cargo hypersig new my-app --host server|cloudflare|vercel writes a project for a container host, Cloudflare Workers, or a Vercel Function, and cargo hypersig add <host> adds one to an existing project (Deploying). Security lists what hypersig checks on each request and what each application checks itself, starting with who may call each action.
What the compiler does not check
The compiler checks that names, types, and links match. These mistakes still compile, and show up only when the program runs. The documentation has the same list.
| Mistake | What happens |
|---|---|
The function routes! generates is never passed to the router |
Its pages and actions answer 404 |
A block under a Base is served neither inside Router::nest at that base nor behind a proxy that strips it |
Its pages and actions answer 404, since its links start with the base and its routes do not |
A page under a Base calls an action of a routes! block without it |
The call answers 404 |
One node_ref!, dialog_ref!, Id, or FileInput name goes on two elements, or El::id is used inside a list template |
Rendering the page panics. Two ids from Id::from_text alike are not caught, and a reference to that id reaches the first |
label_for or a similar reference names an Id no element carries |
The control has no label |
A script outside hypersig finds an El::foreign element by its value, and the constant is renamed |
The script finds no element. Look it up by Id instead |
A #[signals(computed)] field is never passed to Page::computed |
Rendering the page panics, for a field of a nested struct only where the page reads it |
| A list item is used outside the closure it was passed to | Building or rendering the page panics |
| A signal holds an integer beyond ±2^53 | Rendering the page or building the patch panics |
What it does not suit
Without the server, the browser runs only the operations hypersig provides and the scripts a page loads. A JavaScript library such as a map or an editor runs inside a custom element, and cargo hypersig elements writes the names from its custom-elements.json as Rust items (JavaScript libraries). hypersig is a poor fit for:
- An application whose main work runs inside a JavaScript library, such as a drawing tool or a spreadsheet
- An application that works without a connection to the server
- A page whose forms must work with JavaScript turned off, since a form calls an action only through the runtime
Development
CONTRIBUTING.md covers bug reports and pull requests, and AGENTS.md describes the repository layout and the checks a change runs.
License
MIT
Explore related projects
freeCodeCamp/freeCodeCamp
freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.
458,937 starsdonnemartin/system-design-primer
Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards.
377,568 starsreact/react
The library for web and native user interfaces.
252,480 starsvuejs/vue
This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
212,624 stars