rs-yew-crate
Yew Framework (v0.22)
Yew is a modern Rust framework for creating multi-threaded frontend web applications compiled to WebAssembly. It uses a virtual DOM, component-based architecture, and a JSX-like html! macro. MSRV: Rust 1.84.0.
Project Setup
Prerequisites
rustup target add wasm32-unknown-unknown
cargo install --locked trunk
New Project (manual)
cargo new yew-app && cd yew-app
Cargo.toml:
[package]
name = "yew-app"
version = "0.1.0"
edition = "2021"
[dependencies]
yew = { version = "0.22", features = ["csr"] }
Feature
csrenablesRendererand client-side rendering code. Omit for library crates. Usessrfor server-side rendering. Useserdefor serde integration onAttrValue.
index.html (project root):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Yew App</title>
</head>
<body></body>
</html>
src/main.rs:
use yew::prelude::*;
#[component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
move |_| { counter.set(*counter + 1); }
};
html! {
<div>
<button {onclick}>{ "+1" }</button>
<p>{ *counter }</p>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
trunk serve --open
Starter Template
cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template
trunk serve
Trunk Configuration (Trunk.toml)
[serve]
address = "127.0.0.1"
port = 8080
Function Components
The recommended component model. Annotate with #[component], name in PascalCase, return Html.
use yew::{component, html, Html};
#[component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// Usage:
html! { <HelloWorld /> }
Properties (Props)
Derive Properties + PartialEq. Use AttrValue instead of String for attribute values (cheap to clone).
use yew::{component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub name: AttrValue,
#[prop_or_default]
pub is_loading: bool,
#[prop_or(AttrValue::Static("default"))]
pub label: AttrValue,
#[prop_or_else(|| AttrValue::from("computed"))]
pub dynamic: AttrValue,
}
#[component]
fn Greeting(&Props { ref name, is_loading, .. }: &Props) -> Html {
if is_loading { return html! { "Loading" }; }
html! { <p>{"Hello, "}{name}</p> }
}
// Usage:
html! { <Greeting name="Alice" is_loading=false /> }
Children: Use children: Html field name in props to receive nested content.
#[derive(Properties, PartialEq)]
pub struct ContainerProps {
pub children: Html,
}
#[component]
fn Container(props: &ContainerProps) -> Html {
html! { <div class="wrapper">{ props.children.clone() }</div> }
}
Auto-props (yew-autoprops crate): Generate Properties struct from function args.
use yew_autoprops::autoprops;
// #[autoprops] must appear BEFORE #[component]
#[autoprops]
#[component]
fn Greetings(#[prop_or_default] is_loading: bool, message: &AttrValue) -> Html {
html! { <>{message}</> }
}
Callbacks
Callback<IN, OUT> wraps Fn in Rc. Use Callback::from(|e| ...) for event handlers. Pass down as props for child-to-parent communication.
// Passing callbacks as props
#[derive(Properties, PartialEq)]
pub struct ButtonProps {
pub on_click: Callback<Video>,
}
// Creating callbacks with state
let selected = use_state(|| None);
let on_select = {
let selected = selected.clone();
Callback::from(move |video: Video| selected.set(Some(video)))
};
Hooks
Rules: (1) name starts with use_, (2) call only at top-level of component/hook, (3) same call order every render.
Pre-defined hooks:
use_state/use_state_eq— reactive local stateuse_reducer/use_reducer_eq— complex state via reducer patternuse_effect/use_effect_with— side effects (not run during SSR)use_memo— memoized computationuse_callback— memoized callbackuse_context— consume context valueuse_ref/use_mut_ref— persistent mutable referenceuse_node_ref— DOM node referenceuse_force_update— manual re-render trigger
// State
let counter = use_state(|| 0);
counter.set(*counter + 1);
// Effect with deps (runs when deps change)
{
let data = data.clone();
use_effect_with(deps, move |_| {
// setup logic
|| () // cleanup
});
}
// Reducer
use yew::prelude::*;
enum Action { Increment, Decrement }
struct State { count: i32 }
impl Reducible for State {
type Action = Action;
fn reduce(self: Rc<Self>, action: Action) -> Rc<Self> {
match action {
Action::Increment => Self { count: self.count + 1 }.into(),
Action::Decrement => Self { count: self.count - 1 }.into(),
}
}
}
let state = use_reducer(|| State { count: 0 });
state.dispatch(Action::Increment);
Custom hooks: Annotate with #[hook], compose from built-in hooks.
use yew::prelude::*;
#[hook]
pub fn use_toggle(initial: bool) -> (bool, Callback<()>) {
let state = use_state(move || initial);
let toggle = {
let state = state.clone();
Callback::from(move |_| state.set(!*state))
};
(*state, toggle)
}
html! Macro
For detailed syntax rules, element attributes, conditional rendering, fragments, and lists, see references/html_and_events.md.
Key rules:
- Single root node required (use
<>...</>fragments for multiple) - String literals must be quoted and wrapped in braces:
{ "text" } - Tags must self-close (
<br />) or have matching close tags - Rust expressions go in
{ ... } if/if letfor conditional renderingfor item in iter { ... }for lists (addkeyprop)
html! {
<>
<h1>{ "Title" }</h1>
if show_subtitle {
<p>{ "Subtitle" }</p>
}
for item in &items {
<li key={item.id}>{ &item.name }</li>
}
</>
}
Routing
Add yew-router crate. See references/routing.md for nested routers, basename, and query parameters.
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/post/:id")]
Post { id: String },
#[not_found]
#[at("/404")]
NotFound,
}
fn switch(route: Route) -> Html {
match route {
Route::Home => html! { <h1>{"Home"}</h1> },
Route::Post { id } => html! { <p>{format!("Post {}", id)}</p> },
Route::NotFound => html! { <h1>{"404"}</h1> },
}
}
#[component]
fn App() -> Html {
html! {
<BrowserRouter>
<Switch<Route> render={switch} />
</BrowserRouter>
}
}
Navigation: <Link<Route> to={Route::Home}>{"Home"}</Link<Route>> or use_navigator() hook.
Contexts
Provide shared state without prop drilling. See references/contexts_and_state.md.
#[derive(Clone, PartialEq)]
struct Theme { foreground: String, background: String }
// Provider
#[component]
fn App() -> Html {
let theme = Theme { foreground: "#000".into(), background: "#fff".into() };
html! {
<ContextProvider<Theme> context={theme}>
<ThemedButton />
</ContextProvider<Theme>>
}
}
// Consumer
#[component]
fn ThemedButton() -> Html {
let theme = use_context::<Theme>().expect("no theme context");
html! { <button style={format!("color: {}", theme.foreground)}>{"Click"}</button> }
}
Data Fetching
Use gloo-net for HTTP, serde for deserialization, wasm-bindgen-futures for async.
[dependencies]
yew = { version = "0.22", features = ["csr", "serde"] }
gloo-net = "0.6"
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen-futures = "0.4"
use gloo_net::http::Request;
use yew::prelude::*;
#[component]
fn App() -> Html {
let data = use_state(Vec::new);
{
let data = data.clone();
use_effect_with((), move |_| {
let data = data.clone();
wasm_bindgen_futures::spawn_local(async move {
let resp: Vec<Item> = Request::get("/api/items")
.send().await.unwrap()
.json().await.unwrap();
data.set(resp);
});
|| ()
});
}
// render data...
html! {}
}
Events and DOM Interaction
See references/html_and_events.md for full event type table and typed event targets.
// Input handling with TargetCast
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[component]
fn TextInput() -> Html {
let value = use_state(String::default);
let oninput = {
let value = value.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
value.set(input.value());
})
};
html! { <input type="text" {oninput} value={(*value).clone()} /> }
}
Server-Side Rendering
See references/ssr.md for SSR setup, hydration, Suspense integration, and component lifecycle during SSR.
Agents (Web Workers)
Offload tasks to web workers for concurrent processing. Use yew-agent crate. Agents support Public (singleton) and Private (per-bridge) modes. Communication via bridges (bidirectional) and dispatchers (unidirectional). Messages serialized with bincode.
Anti-Patterns to Avoid
Stringin props — UseAttrValue(cheap clone viaRc<str>)Vec<T>in props — UseIArray<T>fromimplicit-clone- Interior mutability (
RefCell,Mutex) — Yew can't detect changes; use hooks - Side effects in render — Move to
use_effect/use_effect_with - Missing keys on lists — Always add
keyto iterated elements - Accessing Web APIs during SSR — Isolate in
use_effect(not run server-side)
Ecosystem & Common Dependencies
| Crate | Purpose |
|---|---|
yew-router |
Client-side routing |
gloo |
Ergonomic web APIs (net, timers, storage, events, dialogs) |
gloo-net |
HTTP requests (fetch) |
wasm-bindgen |
Rust ↔ JS interop |
wasm-bindgen-futures |
Async/await in WASM |
web-sys |
Raw Web API bindings |
js-sys |
JavaScript standard built-in bindings |
serde / serde_json |
Serialization |
stylist |
CSS-in-Rust |
yewdux |
Redux-like state management |
yew-hooks |
Community hooks collection |
yew-autoprops |
Auto-generate props structs |
bounce |
State management (Redux/Recoil-inspired) |
implicit-clone |
Cheap-clone types (IString, IArray) |
References
- HTML macro, events, and DOM interaction: references/html_and_events.md
- Routing (yew-router) patterns: references/routing.md
- Contexts and state management: references/contexts_and_state.md
- Server-side rendering and hydration: references/ssr.md
More from padparadscho/skills
rs-ratatui-crate
Build terminal user interfaces (TUIs) in Rust with Ratatui (v0.30). Use this skill whenever working with the ratatui crate for creating interactive terminal applications, including: (1) Setting up a new Ratatui project, (2) Creating or modifying terminal UI layouts, (3) Implementing widgets (lists, tables, charts, text, gauges, etc.), (4) Handling keyboard/mouse input and events, (5) Structuring TUI application architecture (TEA, component-based, or monolithic patterns), (6) Writing custom widgets, (7) Managing application state in a TUI context, (8) Terminal setup/teardown and panic handling, (9) Testing TUI rendering with TestBackend. Also triggers for questions about crossterm event handling in a Ratatui context, tui-input, tui-textarea, or any ratatui-* ecosystem crate.
22js-gnome-extensions
Build, debug, and maintain GNOME Shell extensions using GJS (GNOME JavaScript). Covers extension anatomy (metadata.json, extension.js, prefs.js, stylesheet.css), ESModule imports, GSettings preferences, popup menus, quick settings, panel indicators, dialogs, notifications, search providers, translations, and session modes. Use when the user wants to: (1) Create a new GNOME Shell extension, (2) Add UI elements like panel buttons, popup menus, quick settings toggles/sliders, or modal dialogs, (3) Implement extension preferences with GTK4/Adwaita, (4) Debug or test an extension, (5) Port an extension to a newer GNOME Shell version (45-49+), (6) Prepare an extension for submission to extensions.gnome.org, (7) Work with GNOME Shell internal APIs (Clutter, St, Meta, Shell, Main).
7js-gnome-apps
Build native GNOME desktop applications using JavaScript (GJS) with GTK 4, Libadwaita, and the GNOME platform. Use when the user wants to create, modify, or debug a GNOME app written in JavaScript/GJS, including UI design with XML or Blueprint, GObject subclassing, Meson build setup, Flatpak packaging, or any task involving GJS bindings for GLib/GIO/GTK4/Adw libraries. Also use when working with `.ui` files, `meson.build`, GResource XML, GSettings schemas, `.desktop` files, or Flatpak manifests in a GJS project context.
6js-stellar-sdk
Guide for building applications with the Stellar JS SDK (@stellar/stellar-sdk). Use when working with the Stellar blockchain in JavaScript/TypeScript — including sending payments, creating accounts, issuing assets, managing trustlines, trading on the DEX, querying Horizon, interacting with Stellar RPC, streaming events, building and signing transactions, multisig, claimable balances, sponsored reserves, SEP-10 auth, federation, fee-bump transactions, and interacting with Soroban smart contracts via the JS SDK. Covers all @stellar/stellar-sdk usage patterns (Horizon module, rpc module, contract module, TransactionBuilder, Keypair, Operation, Asset, etc.).
5rs-soroban-sdk
Expert guidance for building smart contracts on Stellar using the Soroban Rust SDK. Use this skill when working with Soroban smart contracts for tasks including (1) creating new contracts with [contract] and [contractimpl] attributes, (2) implementing storage with Persistent, Temporary, or Instance storage types, (3) working with auth contexts and authorization, (4) handling tokens and Stellar Asset Contracts, (5) writing tests with testutils, (6) deploying contracts, (7) working with events and logging, (8) using crypto functions, (9) debugging contract errors, (10) security best practices and vulnerability prevention, (11) avoiding common security pitfalls like missing authorization, integer overflow, or reinitialization attacks.
5js-stronghold-sdk
Guide for integrating and building with the Stronghold Pay JS SDK and REST API for payment processing. Use when working with Stronghold Pay payment integration, accepting ACH/bank debit payments, linking bank accounts, creating charges/tips, generating PayLinks, or building checkout flows — in sandbox or live environments. Covers Stronghold.Pay.JS drop-in UI, REST API v2 endpoints, PayLink hosted payment pages, customer token management, and payment source handling.
3