Uibeam
A struct-based, JSX-style Web UI library for Rust
Install / Use
/learn @ohkami-rs/UibeamREADME
UI!: JSX-style template syntax with compile-time checksBeam: Component system based on Rust structs
Features
- Struct-based component model.
- Client components via island architecture in Wasm. (experimental) (See Client Component section below)
- Best effort to generate efficient code for template rendering with less redundant memory allocation.
Additionally, HTML completions and hovers in UI! by VSCode extension. (searchuibeam in the extension marketplace)

Usage
[dependencies]
uibeam = "0.4"
When using uibeam just as a template engine, disabling client default feature is recommended
to eliminate useless dependencies:
[dependencies]
uibeam = { version = "0.4", default-features = false }
UI! syntax
use uibeam::UI;
fn main() {
let user_name = "foo";
let style = "
color: red; \
font-size: 20px; \
";
let ui: UI = UI! {
<p class="hello" style={style}>
"Welcome to the world of UIBeam!"
<br>
"こんにちは"
<a
class="user"
style="color: blue;"
data-user-id="123"
href="https://example-chatapp.com/users/123"
>
"@"{user_name}"!"
</a>
</p>
};
println!("{}", uibeam::shoot(ui));
}
unsafely insert HTML string
raw string literal (r#"..."#) or unsafe block contents are rendered without HTML-escape :
use uibeam::UI;
fn main() {
println!("{}", uibeam::shoot(UI! {
<html>
<body>
/* ↓ wrong here: scripts are html-escaped... */
<script>
"console.log('1 << 3 =', 1 << 3);"
</script>
<script>
{include_str!("index.js")}
</script>
/* ↓ scripts are NOT html-escaped, rendered as they are */
<script>
r#"console.log('1 << 3 =', 1 << 3);"#
</script>
<script>
unsafe {include_str!("index.js")}
</script>
<script>
unsafe {"console.log('1 << 3 =', 1 << 3);"}
</script>
</body>
</html>
}));
}
conditional & iterative rendering
{} at node-position in UI! can render, in addition to Display-able values, any impl IntoIterator<Item = UI>. This includes Option<UI> or any other iterators yielding UIs !
use uibeam::{UI, Beam};
struct Task {
id: u64,
title: String,
subtasks: Vec<String>,
completed: bool,
}
fn main() {
let t = Task {
id: 42,
title: "try uibeam".to_string(),
subtasks: vec![],
completed: false,
};
let ui = UI! {
<div id={format!("task-{}", t.id)}>
<h2>{t.title}</h2>
<h3>"subtasks"</h3>
<ul>
{t.subtasks.iter().map(|s| UI! {
<li>{s}</li>
})}
</ul>
{t.completed.then_some(UI! {
<i><strong>"completed"</strong></i>
})}
</div>
};
println!("{}", uibeam::shoot(ui));
}
Beam - Component with Rust struct and JSX-like syntax
use uibeam::{Beam, UI};
struct Layout {
title: String,
children: UI, // `children` field
}
impl Beam for Layout {
fn render(self) -> UI {
UI! {
<html>
<head>
<title>{self.title}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css">
</head>
<body class="bg-gray-100">
{self.children}
</body>
</html>
}
}
}
struct AdminPage {}
impl Beam for AdminPage {
fn render(self) -> UI {
UI! {
<main class="container mx-auto flex-grow py-8 px-4">
<section class="bg-white shadow-md rounded-lg p-6">
<h1 class="text-2xl font-bold text-gray-800 mb-6">
"Password"
</h1>
<form method="post" action="" class="w-full">
<div class="flex flex-col gap-4">
<div class="flex flex-col">
<label for="adminPassword" class="text-gray-700 text-sm font-bold mb-1">
"password"
</label>
<input
required
type="password"
id="adminPassword"
name="adminPassword"
class="py-2 px-3 border border-gray-400 rounded focus:outline-none focus:shadow-outline"
/>
</div>
</div>
<div class="mt-6">
<button
type="submit"
class="bg-purple-500 hover:bg-purple-700 text-white py-2 px-4 rounded focus:outline-none focus:shadow-outline"
>
"Send"
</button>
</div>
</form>
</section>
</main>
}
}
}
fn main() {
let ui = UI! {
<Layout title="admin page"> // title: ("admin page").into()
<AdminPage /> // children: (AdminPage {}).render()
</Layout>
};
println!("{}", uibeam::shoot(ui));
}
Client Component - Wasm islands
overview
#[client] makes your Beam a Wasm island : initially rendered on server, sent with serialized props, and hydrated with deserialized props on browser.
Signal, computed, effect, batch, untracked are available in them.
note
EXPERIMENTAL.
Currently UIBeam's hydration/reactivity system is built upon Preact. This will be rewritten in pure Rust in the future.
usage
working example: examples/counter
-
Activate
"client"feature, and addserdeto your dependencies:[dependencies] uibeam = { version = "0.4" } # `client` is a default feature serde = { version = "1", features = ["derive"] } -
Configure to export all your client components from a specific library crate. (e.g.
lib.rsentrypoint, or another member crate of a workspace)(There's no problem if also including ordinary
Beams in the lib crate.)Additionally, specify
crate-type = ["cdylib", "rlib"]for the crate:[lib] crate-type = ["cdylib", "rlib"] -
Define and use your client components:
/* islands/src/lib.rs */ use uibeam::{UI, Beam}; use uibeam::{Signal, callback, client::PointerEvent}; use serde::{Serialize, Deserialize}; struct CounterButton { on_click: Box<dyn Fn(PointerEvent)>, children: UI, /// additional classes to modify default style class: Option<&'static str>, } #[uibeam::client] // client component, but not Serialize/Deserialize and not at island boundary impl Beam for CounterButton { fn render(self) -> UI { UI! { <button class={self.class.unwrap_or("")} onclick={self.on_click} > {self.children} </button> } } } // client component at **island boundary** must be `Serialize + for<'de> Deserialize<'de>`. #[derive(serde::Serialize, serde::Deserialize)] pub struct Counter { pub initial_count: i32, } // `(island)` means **island boundary** #[uibeam::client(island)] impl Beam for Counter { fn render(self) -> UI { let count = Signal::new(self.initial_count); // callback! - a thin utility for callbacks using signals. let increment = callback!( // [dependent signals, ...] [count], // closure depending on the signals |_| count.set(*count + 1) ); /* << expanded >> let increment = { let count = count.clone(); move |_| count.set(*count + 1) }; */ let decrement = callback!([count], |_| { count.set(*count - 1); }); UI! { <div> <p> "Count: "{*count} </p> <div> <CounterButton on_click={Box::new(decrement)} class={None} >"-"</CounterButton> <CounterButton o
Related Skills
himalaya
335.2kCLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
coding-agent
335.2kDelegate coding tasks to Codex, Claude Code, or Pi agents via background process
