# Yew Framework Documentation for AI Coders: Core Concepts & Coding
This document provides a comprehensive overview of the Yew framework's core coding concepts, designed to equip AI robot coders with the knowledge necessary to build web applications using Rust and WebAssembly.
Yew is a modern Rust framework for building client-side web applications using WebAssembly (Wasm). It enables the development of highly performant web UIs by leveraging Rust's strong type system and rich ecosystem. Yew promotes a component-based architecture for building reusable and maintainable UI elements.
## Fundamental Building Blocks
### The `html!` Macro for UI Composition
Yew employs the `html!` procedural macro for declarative UI construction, drawing inspiration from JSX. This macro is the primary way to define the structure of your component's output.
**Syntax and Features:**
* **Single Root Node:** The `html!` macro always expects a single root HTML node. To render multiple top-level elements without a wrapping container, use the fragment syntax: `<> ... >`.
* **Embedding Rust Expressions:** Any valid Rust expression can be embedded within the markup using curly braces (`{ expression }`). The expression **must evaluate to a type that implements `Into`**.
```rust
let header_text = "Welcome to Yew".to_string();
let count = 5;
html! {
<>
{ header_text }
{"Current count: "}{ count }
>
}
```
* **Literals:** String literals are typically enclosed in quotes and then a `{}`, e.g., `{"Hello"}`. They are treated as `Text` nodes, inherently mitigating common HTML injection (XSS) risks.
* **Element / Component Definition:**
* **HTML Elements:** Standard HTML elements are written as `child` or self-closing ``.
* **Dynamic Tag Names:** For situations where the HTML tag name is determined at runtime, use the `@{expression}` syntax. The expression must be a string.
```rust
let level = 3;
html! { <@{format!("h{}", level)} class="subtitle">{"Dynamic Heading"}@> }
```
* **Yew Components:** Yew components are instantiated like custom HTML tags, using PascalCase for their names: ``.
* **Attributes and Properties:**
* **HTML Attributes:** Set on elements directly: ``.
* **Boolean Attributes:** Set with `true` or `false`. `false` is equivalent to omitting the attribute entirely.
```rust
html! { }
```
* **String-like Attributes:** Can accept `&str`, `String`, or Yew's optimized `AttrValue` (a cheaply cloneable `Rc` or `&'static str`). `AttrValue` is generally recommended for performance-sensitive scenarios, especially when passing values as properties to other components.
* **Optional Attributes:** Use `Option` for attribute values. If the `Option` is `None`, the attribute will not be rendered in the DOM.
```rust
let maybe_id: Option<&str> = Some("unique-element");
html! { } // Renders with id or not
```
* **Yew-Specific Properties (Special Props):** These are not directly reflected in the DOM but serve as instructions to Yew's Virtual DOM.
* `ref={node_ref_handle}`: Connects a `NodeRef` to a DOM element, allowing direct programmatic access to the underlying DOM node (e.g., for canvas manipulation, scrolling, form input values).
* `key={unique_key}`: Provides a unique identifier for elements within a list. Crucial for performance optimization, as Yew uses keys to efficiently reconcile list items during updates, preventing unnecessary re-renders or DOM manipulations. Keys must be unique *within their immediate siblings* (the list itself) and should be stable/deterministic, not based on item position.
* **Conditional Rendering:** Use standard Rust `if` and `if let` control flow structures directly within the `html!` macro to conditionally render content.
```rust
let show_message = true;
html! {
if show_message {
{"This message is visible."}
} else {
{"Message hidden."}
}
}
```
* **List Rendering / Iteration:**
* Use `for` syntax directly in `html!`: `{ for collection.iter() }`. This expects the iterator items to be renderable `Html`.
* Alternatively, map the collection to `Html` elements and use `.collect::()`.
* **Always use `key` for list items** where the order or presence of items can change, as this drastically improves reconciliation performance.
```rust
let items = vec!["Apple", "Banana", "Cherry"];
html! {
{ for items.iter().map(|item| html! {
{item}
}) }
}
```
### Components: Function Components
Yew applications are built from components, which encapsulate UI logic and presentation. Function components are the recommended and most common way to define components in modern Yew.
* **Definition:** Declare a function component using the `#[function_component]` attribute on a `fn` that returns `Html`. By convention, component names are PascalCase.
```rust
use yew::prelude::*;
#[function_component]
fn MySimpleComponent() -> Html {
html! {
{"Hello from a component!"}
}
}
```
* **Properties (Props):** Data is passed from parent to child components using "props". Props are defined by a struct that must implement `Properties` (usually via `#[derive(Properties)]`) and `PartialEq`. The function component accepts an `&Props` reference as its single argument.
```rust
#[derive(Properties, PartialEq)]
pub struct GreetProps {
pub name: String,
#[prop_or_default] // Field attribute for optional props with default value
pub greeting_text: String,
}
#[function_component]
fn Greeter(props: &GreetProps) -> Html {
let greeting = if props.greeting_text.is_empty() {
"Hello".to_string()
} else {
props.greeting_text.clone()
};
html! {
{ format!("{}, {}!", greeting, props.name) }
}
}
// Usage: html! { } or html! { }
```
* **Reactive Nature of Props:** Yew automatically re-renders a component when its props change (detected via `PartialEq`).
* **`props!` Macro:** Allows building `Properties` structs programmatically.
```rust
use yew::props;
let my_props = props! { GreetProps { name: "Charlie".to_string() } };
html! { }
```
* **Children (Special Prop):** If a component's `Props` struct includes a `pub children: Html` field, it can accept nested `html!` content.
```rust
#[derive(Properties, PartialEq)]
pub struct CardProps {
pub title: String,
pub children: Html, // This field name is special
}
#[function_component]
fn Card(props: &CardProps) -> Html {
html! {
{ &props.title }
{ props.children.clone() }
}
}
// Usage: html! {
Some content here.
}
```
* **Generic Components:** Function components can be generic over types, provided the generic type parameters meet the necessary trait bounds (e.g., `PartialEq`).
```rust
#[derive(Properties, PartialEq)]
pub struct ItemDisplayProps {
pub item: T,
}
#[function_component]
pub fn ItemDisplay(props: &ItemDisplayProps) -> Html
where
T: PartialEq + ToHtml + 'static, // 'static for use in VDOM
{
html! {
{ &props.item }
}
}
// Usage: html! { item=123 /> }
```
* **Pure Components:** A function component is "pure" if its output `Html` is solely determined by its props, and it has no side effects or internal mutable state. Yew's reconciliation benefits from pure components.
* Simple pure components with no hooks can sometimes be implemented as regular functions returning `Html` to reduce overhead.
* **Communication Patterns:**
* **Parent to Child:** Via [Props](#properties-props).
* **Child to Parent:** Via [Callbacks](#callbacks). The parent passes a `Callback` to the child via props, and the child calls `emit()` on it to send data back up.
## State Management and Interactivity
Yew provides "hooks" to manage mutable state and side effects within function components.
### Hooks
Hooks are functions that allow "hooking into" the lifecycle and state management capabilities of function components.
**Rules of Hooks:**
1. **Naming Convention:** Hook function names **must start with `use_`**.
2. **Top-Level Calls Only:** Hooks can **only be called at the top level of a function component or another hook**, and not inside loops, conditionals without `if let`, or nested functions *unless* within the scrutinee of a top-level `if` or `match` expression.
3. **Consistent Call Order:** Hooks must be called in the exact same order on every render. This enables Yew to correctly associate state with hook calls.
4. **No Early Return:** A component using hooks cannot `return` early before all hooks are called unless using [Suspense](#suspense).
**Common Pre-defined Hooks:**
* **`use_state() -> UseStateHandle`:** Manages local component state. Returns a handle that can be dereferenced to get the current value and has a `.set(new_value)` method to update the state. Updating state triggers a re-render.
```rust
use yew::prelude::*;
#[function_component]
fn ClickCounter() -> Html {
let count = use_state(|| 0); // Initializes with 0
let onclick = {
let count = count.clone(); // Clone the handle for the closure
move |_| {
count.set(*count + 1); // Update the state
}
};
html! { }
}
```
* **`use_state_eq() -> UseStateHandle`:** Similar to `use_state`, but only triggers a re-render if the new value is *not* equal to the current value (using `PartialEq`).
* **`use_memo(f: impl FnOnce(D) -> T, deps: D) -> Rc`:** Memoizes an expensive computation. The `f` closure is only re-executed if `deps` (dependencies) change.
* **`use_callback(f: F) -> Callback`:** Memoizes a `Callback` instance. The `Callback` is only recreated if its dependencies (captured variables) change.
* **`use_mut_ref() -> Rc>`:** Provides a mutable reference (`RefCell`) that persists across re-renders without triggering them. Useful for mutable data that doesn't directly affect rendering or for interop with mutable JS APIs.
* **`use_node_ref() -> NodeRef`:** Creates a `NodeRef` handle, used to get a direct reference to a rendered DOM element.
```rust
use yew::prelude::*;
use web_sys::HtmlInputElement;
#[function_component]
fn MyInput() -> Html {
let input_ref = use_node_ref();
let current_value = use_state(|| String::new());
let on_input_change = {
let input_ref = input_ref.clone();
let current_value = current_value.clone();
move |_| {
if let Some(input) = input_ref.cast::() {
current_value.set(input.value());
}
}
};
html! {
{"Input Value: "}{&*current_value}
}
}
```
* **`use_reducer() -> UseReducerHandle`:** Manages more complex state using a reducer pattern (similar to Redux), allowing state updates based on dispatched "actions". The state must implement the `Reducible` trait.
* **`use_reducer_eq() -> UseReducerHandle`:** Similar to `use_reducer`, but dispatches only if the new state differs from the old.
* **`use_effect(f: impl FnOnce() -> impl FnOnce())`:** Runs a side effect after every render. Can return a cleanup closure that runs before the next effect or when the component is unmounted.
* **`use_effect_with(deps: D, f: impl FnOnce(D) -> impl FnOnce())`:** Runs a side effect only when `deps` (dependencies) change.
* **`use_context() -> Option>`:** Accesses a context value provided by an ancestor `ContextProvider` component.
* **`use_force_update()`:** Returns a callback that, when emitted, forces a re-render of the component. Use sparingly.
**Custom Hooks:** Components can extract reusable stateful logic into custom hooks by defining functions starting with `use_` and marking them with `#[hook]`. Custom hooks compose existing hooks.
### Callbacks
`Callback` is a crucial type for event handling and child-to-parent communication. It wraps an `Fn` closure in an `Rc`, making it cheaply clonable.
* **`Callback::from(closure)`:** Creates a `Callback` from a closure.
* **`callback.emit(value)`:** Invokes the wrapped closure with the given value.
* **DOM Events:** Event handlers in `html!` (e.g., `onclick`, `oninput`) expect a `Callback` that takes the corresponding `web_sys` event type as an argument.
```rust
use yew::prelude::*;
use web_sys::MouseEvent;
#[function_component]
fn MyButton() -> Html {
let onclick_handler = Callback::from(move |e: MouseEvent| {
// Access event properties:
log::info!("Click event at: ({}, {})", e.client_x(), e.client_y());
// More complex logic...
});
html! { }
}
```
* **`TargetCast` Trait:** Provided by Yew (within `yew::prelude::*`), this trait extends `web_sys::Event` to safely cast the event target to a specific HTML element type (e.g., `HtmlInputElement`).
* `event.target_dyn_into::() -> Option` (safe, checked)
* `event.target_unchecked_into::() -> HtmlElementType` (unchecked, use with caution when type is guaranteed)
### Contexts
Contexts provide a way to pass data deeply through the component tree without manually "prop drilling" at every level.
* **Provider (`ContextProvider`):** An ancestor component wraps its children with `ContextProvider`, providing a value of type `T`. `T` must implement `Clone` and `PartialEq`.
```rust
// Define your context data
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
#[function_component]
fn ThemeProvider(props: &ChildrenProps) -> Html { // ChildrenProps from yew::html
let theme = use_state(|| Theme {
foreground: "#000".to_string(),
background: "#eee".to_string(),
});
html! {
context={(*theme).clone()}>
{ props.children.clone() }
>
}
}
// Usage: html! { }
```
* **Consumer (`use_context` hook):** Descendant function components use `use_context::()` to retrieve the provided value.
```rust
#[function_component]
fn ThemedText() -> Html {
let theme = use_context::() // Retrieve the Theme context
.expect("Theme context not provided!");
html! {
{"This text is themed."}
}
}
```
* **Mutable Contexts:** To allow children to modify a context value, combine `ContextProvider` with `use_reducer` for a predictable state update mechanism.
### Event Handling and Delegation
Yew integrates with `web-sys` for DOM events. Yew's event system employs event delegation: listeners are not directly attached to individual elements but are handled by a single delegate at the application's root. Events then "bubble up" through Yew's Virtual DOM hierarchy.
* **Event Listener Names:** In `html!`, event listeners start with `on` followed by the event name (e.g., `onclick`, `oninput`).
* **Manual Event Listeners:** For events not directly supported by `html!` or for fine-grained control, use `use_effect_with` and `gloo-events` (`EventListener`) to manually attach/detach event listeners to `NodeRef`-obtained DOM elements.
```rust
use gloo::events::EventListener;
use yew::prelude::*;
use web_sys::HtmlElement;
#[function_component]
fn CustomEventHandler() -> Html {
let div_ref = use_node_ref();
use_effect_with(div_ref.clone(), {
let div_ref_clone = div_ref.clone();
move |_| {
let mut listener_obj: Option = None;
if let Some(div_element) = div_ref_clone.cast::() {
let listener = EventListener::new(&div_element, "custom-event", move |event| {
log::info!("Custom event received!");
// event.dyn_into::() for richer data
});
listener_obj = Some(listener);
}
move || drop(listener_obj) // Cleanup when effect re-runs or component unmounts
}
});
html! {
{"Div with custom event listener"}
}
}
```
## Advanced Topics
### Interacting with JavaScript (JS) and Web APIs
Yew compiles to Wasm, but direct interaction with browser APIs often involves `wasm-bindgen` and related crates.
* **`wasm-bindgen`:** Bridges calls between Rust and JavaScript. Used for defining FFI (Foreign Function Interface) to import/export functions.
* **`web-sys`:** Provides Rust bindings for all Web APIs (DOM, Fetch, etc.). This is the primary way to interact with the browser from Rust.
* **Features:** `web-sys` is heavily feature-gated; enable only the necessary features in `Cargo.toml` to avoid bloat (e.g., `features = ["Document", "HtmlElement", "Window"]`).
* **Inheritance:** `web-sys` types simulate JavaScript inheritance using Rust's `Deref` and `AsRef` traits. For instance, an `HtmlElement` can deref to `Element`, then `Node`, then `EventTarget`, finally `JsValue`. This allows calling methods from ancestor types.
* **`JsCast` Trait:** Crucial for downcasting `JsValue` or generic `EventTarget` (from `web_sys`) to specific, more concrete types (e.g., `HtmlInputElement`). Provides `dyn_into` (checked, returns `Result`) and `unchecked_into` (unchecked, faster).
* **`js-sys`:** Provides Rust bindings for JavaScript's standard, built-in objects (e.g., `Date`, `Object`).
* **`wasm-bindgen-futures`:** Bridges Rust `Future`s with JavaScript `Promise`s.
* **`spawn_local(future)`:** Spawns a Rust `Future` to run on the current thread's event loop, enabling asynchronous operations (e.g., `async fetch()` calls).
### Asynchronous Operations and Suspense
Yew's `Suspense` component allows suspending component rendering while waiting for an asynchronous task (e.g., data fetching) to complete, showing a fallback UI in the interim. This enables a "Render-as-You-Fetch" pattern.
* **`Suspense` Component:** Wraps children components that might "suspend". Requires a `fallback` prop, which is the `Html` to render during suspension.
* **Suspending Hooks:** A hook can signal suspension by returning `Err(Suspension)`.
* **`Suspension::new()`:** Creates a `Suspension` and a `SuspensionHandle`. When `handle.resume()` is called (or `handle` is dropped), the suspended component re-renders.
```rust
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
// A hypothetical async data loading function
async fn fetch_user_data() -> String { "AI Robot Coder".to_string() }
// This hook will suspend rendering until user data is fetched
#[hook]
fn use_current_user() -> SuspensionResult {
let (suspension, handle) = Suspension::new(); // Create suspension
let user_data_state = use_state(|| None ); // Local state for fetched data
// If data is already there, return Ok.
if let Some(data) = &*user_data_state {
return Ok(data.clone());
}
// If not, spawn an async task and signal suspension.
let user_data_state_clone = user_data_state.clone();
wasm_bindgen_futures::spawn_local(async move {
let data = fetch_user_data().await;
user_data_state_clone.set(Some(data)); // Update state
handle.resume(); // Signal the suspension to resume
});
Err(suspension) // Signal that the component should suspend
}
#[function_component]
fn UserDisplay() -> HtmlResult { // HtmlResult instead of Html for suspending components
let user_name = use_current_user()?; // The '?' operator propagates the suspension
Ok(html! {