fusor-core 0.1.5

Signals, component ownership and the browser DOM runtime for fusor applications
Documentation

Build reactive web apps with Rust

Write your UI in HTML files. Keep state and frontend logic in Rust. No markup inside Rust macros.

fusor compiles your Rust to WebAssembly for the browser. When a signal changes, the bindings that read it run again.

In development · v0.1 — This is experimental software. APIs will change; don't rely on it for production yet. See status and known limitations.

Get started · How it works · Examples · Status · Contributing

A component in two files

The view is an HTML file. Bindings and event handlers are Rust expressions:

<!-- web/components/counter.html -->
<template rust:component="Counter">
  <div class="counter">
    <output aria-live="polite">{{ state.count.get() }}</output>
    <div class="counter-actions">
      <button on:click="state.increment()">Increment</button>
      <button on:click="state.reset()">Reset</button>
    </div>
  </div>
</template>

The Rust module owns the state and behavior, and connects them to the HTML with template!:

// src/counter.rs
use fusor::prelude::*;

#[derive(FromInputs)]
pub struct Counter {
    #[local(init = signal(0))]
    count: Signal<i32>,
}

impl Counter {
    fn increment(&self) {
        self.count.update(|n| *n += 1);
    }

    fn reset(&self) {
        self.count.set(0);
    }
}

fusor::template!("web/components/counter.html");

rust:component="Counter" makes the struct's fields and methods available as state. Each button calls one of its methods, which changes the count signal. The output reads that signal, so its text updates when the count changes. The compiler checks the expressions in the HTML along with the rest of the module.

These two files are a complete reusable component, not a whole app. A page places it with a <Counter></Counter> tag inside <App>, which starts the app and owns everything in it. The landing page's example editor shows, next to each component's files, a short page that mounts it; for this counter, that is index.html and its Rust module. fusor new creates Cargo.toml, build.rs, and src/lib.rs; declare additional component modules in src/lib.rs. The documentation site has a full walkthrough of templates, modules, and application startup.

Get started

The v0.1.0 CLI and crates are released. To build apps you need Rust 1.85 or newer, installed with rustup. Then install the CLI:

# macOS or Linux
curl -fsSL https://fusor.build/install.sh | sh
# Windows PowerShell
irm https://fusor.build/install.ps1 | iex

The installer downloads the latest release, verifies its checksum, and puts fusor in .fusor/bin under your home directory. On macOS and Linux it prints the line to add to your PATH and leaves your shell configuration alone. On Windows it adds the directory to your user PATH; open a new terminal before running fusor. The scripts are install.sh and install.ps1 in this repository if you want to read them first.

Create and run an app:

fusor new my-app
cd my-app
fusor dev

Open the URL printed by fusor dev (normally http://127.0.0.1:4173). The starter has a working counter component. Edit its HTML or Rust and the dev server rebuilds the app. Run fusor check for compiler errors or fusor build for a static build in dist/. The installation guide covers requirements and troubleshooting in more detail.

The documentation app contains the installation walkthrough and authoring guides. To run it from a repository checkout:

cargo fusor dev -p fusor-docs

How it works

fusor keeps the boundaries visible:

  • HTML stays HTML. Components use native markup with Rust expressions for text, attributes, conditions, lists, and events.
  • Rust stays Rust. State, methods, and imports live in ordinary modules. Cargo and rustc check the generated bindings alongside your code.
  • Updates follow signal reads. A binding tracks the signals it reads and updates its own DOM target when they change. There is no virtual DOM.
  • Ownership handles cleanup. Removing a component disposes its listeners, subscriptions, and owned children.

The default application is a client side WebAssembly app. Optional packages add routing, async resources, shared queries, background workers, native JavaScript integrations, and server rendered islands. See the guides for their setup and current limits.

Examples

  • Landing app — a complete fusor site with live counter, search, lists, and async examples.
  • Playground — Rust written directly inside an HTML page with reactive bindings.
  • Reader — typed navigation and async data across owned views.
  • Editor — typed fields, explicit saves, and state shared across views.
  • Integrations — CodeMirror and Chart.js connected to Rust state.
  • Workers — background Rust tasks connected to async resources.

The documentation showcase has more runnable examples. Performance measurements and their methodology live in benchmarks.

Status and known limitations

fusor is v0.1 and experimental. Its built-in renderer targets the browser; fusor does not ship a desktop, mobile or terminal renderer.

Breaking changes. Until 1.0, each minor release (0.2, 0.3, …) may change the HTML syntax and the Rust APIs. Patch releases (0.1.x) fix bugs without breaking changes. Every breaking release will come with migration notes.

Bundle size. A hello-world app ships about 54 KiB gzipped, counting its HTML, JavaScript and Wasm. In the same benchmark run, the Leptos equivalent is 25.5 KiB and Solid's is 4 KiB. Size grows with the app, and reducing it is ongoing work.

Editor support. rust-analyzer works in your .rs modules, with completion, hover and go to definition on the fields and methods templates use. Compiler errors in template expressions point at the line in the .html file. There is no completion or hover inside .html files yet.

Not supported yet:

  • SVG, MathML, <select> and table parsing contexts inside <ForEach>, <If> and <Match>. Tables work with an explicit <tbody> around <ForEach>.
  • Match guards and ref/mut captures in <Case> patterns.
  • bind on file inputs. Read their files in an on:change handler.
  • Nested <Async> boundaries, editable controls and router outlets inside coherent async views.
  • Nested islands and streamed server rendering. Server rendering is synchronous, and your application resolves its data first.
  • A built-in HTTP backend or server functions. fusor renders HTML; you bring your own HTTP server and data layer.
  • npm workspaces and linked packages. Apps that use component JavaScript rebuild and reload on every source edit instead of refreshing in place.

The guides list the limits of each feature in more detail.

How is this different from Dioxus?

Dioxus components are Rust functions that return rsx! markup, and Dioxus reconciles their output through a VirtualDom. fusor writes markup in separate .html templates connected to Rust modules with template!. It has no virtual DOM: bindings track the signals they read and update their associated DOM targets.

How is this different from Leptos?

Leptos and fusor share a reactive model: fine-grained signal tracking and no virtual DOM. The difference is authoring. Leptos views are written in Rust, with the view! macro or builder functions. fusor uses separate HTML templates, with Rust expressions in bindings and attributes, connected to Rust modules with template!.

How is this different from Sycamore?

Sycamore is the closest in design: fine-grained reactivity, no virtual DOM, and Rust compiled to WebAssembly. It is also older and more established. The difference is again authoring. Sycamore components are Rust functions that return markup written with the view! macro. In fusor the markup is an .html file, and a Rust type provides its state and methods.

Contributing

fusor is an open source project in early development. The contributing guide covers the repository setup, checks, and development workflow. The framework and tooling live in crates/; runnable applications live in apps/ and examples/.

fusor is built with the help of AI tools, mainly Claude Code, under the same rules the project asks of contributors: see Using AI tools.

License

Licensed under the MIT License.