# 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! {

{ format!("Hello, {}!", user_name) }

}) } #[function_component] fn App() -> Html { html! { {"Loading user..."}

}}>
} } ``` ### Routing (`yew-router` crate) `yew-router` provides client-side routing for Single Page Applications (SPAs). * **`Routable` Enum:** Define routes as an enum deriving `Routable`. Each variant maps to a URL path using `#[at("/path")]`. `#[not_found]` designates the fallback route. * **Path Segments:** Define dynamic segments with `:segment_name` (for single segments) or `*wildcard` (for multi-segment wildcards). These become fields in the enum variant. * **`BrowserRouter`:** The main router component that manages browser history. All `` and `` components must be descendants of `BrowserRouter`. * **`Switch` Component:** Takes an `Routable` enum and a `render` function. The `render` function receives the matched route variant and returns the `Html` to display. * **`Link` Component:** A component that renders an `` tag but performs client-side navigation (`pushState`) instead of a full page reload. * **Navigation API (`use_navigator()`):** Provides programmatic navigation via `navigator.push(&Route)` or `navigator.replace(&Route)`. * **Query Parameters:** Can be included in navigation by passing a serializable struct/map to `push_with_query()` or retrieved from `location.query()`. * **Nested Routers:** Allows for modular routing within sub-sections of the application, often using a `*` wildcard in the parent router to delegate to a child router. * **Basename:** Configure a common prefix for all routes, useful when the application is served from a sub-path. ### Web Workers and Agents Yew `Agents` are a mechanism for offloading tasks to Web Workers, enabling concurrent processing and preventing UI unresponsiveness. * **Types of Agents:** * `Public`: A single instance shared across all bridges in a web worker. * `Private`: A new instance spawned for each bridge connection. * **Communication:** * `Bridges`: Bi-directional communication between a component and an agent, or between agents. * `Dispatchers`: Uni-directional communication from a component to an agent. * **Overhead:** Agents introduce serialization/deserialization overhead for messages between threads (using `bincode`), making them suitable for chunky computations rather than frequent, small messages. ## Deployment Considerations * **Release Build:** Use `trunk build --release` for optimized, production-ready builds. * **Server Configuration:** * **SPA Fallback:** Configure the HTTP server to serve `index.html` as a fallback for any unmatched URL paths, allowing the `yew-router` to handle client-side routing. * **MIME-type:** Ensure `.wasm` files are served with the `application/wasm` MIME-type. * **Relative Paths:** Use `` in `index.html` and `trunk build --public-url /your/path/` to deploy the app under a sub-path. * **Environment Variables:** Use `std::env!("VAR_NAME")` to embed environment variables at **compile time** (runtime access in browser is not direct). ## Debugging and Testing ### Debugging * **Panics:** Yew automatically logs Rust panics to the browser's developer console. * **Console Logging:** * `wasm-logger`: Integrates Rust's `log` crate with the browser console. * `gloo-console`: Provides `log!` macro for direct `JsValue` logging to console. * `tracing-web`: Integrates `tracing` framework with browser console and performance API. * **Component Lifecycles:** Use `tracing` to gain insights into component re-renders and hook execution. * **Source Maps:** Limited support available; requires specific configuration. ### Testing * **`wasm_bindgen_test`:** Enables running Rust tests directly in a browser environment. * **Snapshot Testing:** Yew provides utilities (`yew::tests::layout_tests`) for snapshot testing component output. * **Shallow Rendering:** Work in progress for testing components in isolation without rendering their full subtree. ---