Introduction

What is Yew?

Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.

  • It features a component-based framework which makes it easy to create interactive UIs. Developers who have experience with frameworks like React and Elm should feel quite at home when using Yew.
  • It has great performance by minimizing DOM API calls and by helping developers easily offload processing to background web workers.
  • It supports JavaScript interoperability, allowing developers to leverage NPM packages and integrate with existing JavaScript applications.

Join Us 😊

Our community is thriving!

Ready to dive in?

Click the link below to learn how to build your first Yew app and learn from community example projects

{% page-ref page="getting-started/project-setup/" %}

Still not convinced?

This project is built on cutting edge technology and is great for developers who like to develop the foundational projects of tomorrow. Here are some reasons why we believe that frameworks like Yew are the future of web development.

Wait, why WebAssembly?

WebAssembly (Wasm) is a portable low-level language that Rust can compile into. It runs at native speeds in the browser and is interoperable with JavaScript and supported in all major browsers. For ideas on how to get the most out of WebAssembly for your app, check out this list of Use Cases.

It should be noted that using Wasm is not (yet) a silver bullet for improving the performance of a web app. As of right now, using DOM APIs from WebAssembly is still slower than calling them directly from JavaScript. This is a temporary issue which the WebAssembly Interface Types proposal aims to resolve. If you would like to learn more, check out this excellent article from Mozilla.

Ok, but why Rust?

Rust is blazing fast and reliable with its rich type system and ownership model. It has a tough learning curve but is well worth the effort. Rust has been voted the most loved programming language in Stack Overflow's Developer Survey four years in a row: 2016, 2017, 2018 and 2019.

Rust also helps developers write safer code with its rich type system and ownership model. Say goodbye to hard to track down race condition bugs in JavaScript! In fact, with Rust, most of your bugs will be caught by the compiler before your app even runs. And don't worry, when your app does run into an error, you can still get full stack-traces for your Rust code in the browser console.

Alternatives?

We love to share ideas with other projects and believe we can all help each other reach the full potential of this exciting new technology. If you're not into Yew, you may like the following projects (listed alphabetically)

  • Draco - "A Rust library for building client side web applications with Web Assembly"
  • Percy - "A modular toolkit for building isomorphic web apps with Rust + WebAssembly"
  • Seed - "A Rust framework for creating web apps"
  • Smithy - "A framework for building WebAssembly apps in Rust"

description: Set yourself up for success

Project Setup

Rust

First, you'll need Rust. To install Rust and the cargo build tool, follow the official instructions.

Wasm Build Tools

Extra tooling is needed to facilitate the interop between WebAssembly and JavaScript. Additionally, depending on the tool you choose, they can help make deployment and packaging much less of a headache by generating all of the wrapper JavaScript code necessary to run the .wasm file from your app in the browser.

wasm-pack

A CLI tool developed by the Rust / Wasm Working Group for packaging up WebAssembly. Best used together with the wasm-pack-plugin for Webpack.

{% page-ref page="using-wasm-pack.md" %}

wasm-bindgen

Both a library and CLI tool and is also developed by the Rust / Wasm Working Group. It is a low level tool (used internally by wasm-pack) which facilitates JS / WebAssembly interoperability. We don't recommend using wasm-bindgendirectly because it requires hand-writing some JavaScript to bootstrap you WebAssembly binary. However, it is possible and more info can be found on the wasm-bindgen guide.

{% page-ref page="using-wasm-bindgen.md" %}

cargo-web

The preferred web workflow tool before the introduction of wasm-pack and wasm-bindgen. It is still the quickest way to get up and running and worth installing to run examples that haven't been migrated to support wasm-pack yet.

{% page-ref page="using-cargo-web.md" %}

Comparison

wasm-pack wasm-bindgen cargo-web
Project Status Actively maintained by the Rust / Wasm Working Group Actively maintained by the Rust / Wasm Working Group No Github activity for over 6 months
Dev Experience Almost there! Requires webpack for best experience. Barebones. You'll need to write some scripts to streamline your dev experience. Just works! Batteries included, no external dependencies needed.
Local Server Supported with webpack plugin Not supported Supported
Auto rebuild on local changes Supported with webpack plugin Not Supported Supported
Headless Browser Testing Supported Supported Supported
Supported Targets
  • wasm32-unknown-unknown
  • wasm32-unknown-unknown
  • wasm32-unknown-unknown
  • wasm32-unknown-emscripten
  • asmjs-unknown-emscripten
web-sys Compatible Compatible Incompatible
stdweb Compatible Compatible Compatible
Example Usage Starter template Build script for Yew examples Build script for Yew examples

Using wasm-pack

This tool was created by the Rust / Wasm Working Group and is the most actively developed tool for building WebAssembly applications. It supports packaging code into npm modules and has an accompanying Webpack plugin for easy integration with an existing JavaScript application. Find more information here.

{% hint style="info" %} Note that the crate-type in your Cargo.toml will need to be cdylib when using wasm-pack {% endhint %}

Install

cargo install wasm-pack

Build

This command will produce a bundle in the ./pkg directory with your app's compiled WebAssembly along with a JavaScript wrapper which can be used to start your application.

wasm-pack build --target web

Bundle

For more information on Rollup visit this guide

rollup ./main.js --format iife --file ./pkg/bundle.js

Serve

Feel free to use your preferred server. Here we use a simple python server to serve to http://[::1]:8000.

python -m http.server 8000

Supported Targets

  • wasm32-unknown-unknown

Using wasm-bindgen

{% hint style="info" %} Contribute to our docs: Explain how to use wasm-bindgen to build an app {% endhint %}

Using cargo-web

Cargo web is a cargo subcommand for building client web apps. It makes building and deploying web applications incredibly easy. It is also the only toolchain that supports Emscripten targets. Read more here.

Install

cargo install cargo-web

Build

cargo web build

Run

cargo web start

Supported Targets

  • wasm32-unknown-unknown
  • wasm32-unknown-emscripten
  • asmjs-unknown-emscripten

{% hint style="info" %} For *-emscripten targets, you'll need to install the Emscripten SDK {% endhint %}

Starter Templates

wasm-pack

The important distinction between these templates and using cargo-web is that this approach uses a lib, not a bin crate, and the entry-point to your program is annotated with a #[wasm_bindgen] annotation.

Your Cargo.toml also should specify that you have a "cdylib" crate-type.

{% code title="Cargo.toml" %}

[package]
name = "yew-app"
version = "0.1.0"
authors = ["Yew App Developer <name@example.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
# for web_sys
yew = 0.15
# or for stdweb
# yew = { version = "0.15", package = "yew-stdweb" }
wasm-bindgen = "0.2"

{% endcode %}

Other templates

Build a Sample App

First create a new binary project:

cargo new yew-app && cd yew-app

Add yew and wasm-bindgen to your dependencies (refer here for the latest version)

{% code title="Cargo.toml" %}

[package]
name = "yew-app"
version = "0.1.0"
authors = ["Yew App Developer <name@example.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
yew = "0.15"
wasm-bindgen = "0.2"

{% endcode %}

Copy the following template into your src/lib.rs file:

{% code title="src/lib.rs" %}


#![allow(unused_variables)]
fn main() {
use wasm_bindgen::prelude::*;
use yew::prelude::*;

struct Model {
    link: ComponentLink<Self>,
    value: i64,
}

enum Msg {
    AddOne,
}

impl Component for Model {
    type Message = Msg;
    type Properties = ();
    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            value: 0,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::AddOne => self.value += 1
        }
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        // Should only return "true" if new properties are different to
        // previously received properties.
        // This component has no properties so we will always return "false".
        false
    }

    fn view(&self) -> Html {
        html! {
            <div>
                <button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
                <p>{ self.value }</p>
            </div>
        }
    }
}

#[wasm_bindgen(start)]
pub fn run_app() {
    App::<Model>::new().mount_to_body();
}
}

{% endcode %}

This template sets up your root Component, called Model which shows a button that updates itself when you click it. Take special note of App::<Model>::new().mount_to_body() inside main() which starts your app and mounts it to the page's <body> tag. If you would like to start your application with any dynamic properties, you can instead use App::<Model>::new().mount_to_body_with_props(..).

Finally, add an index.html file into a new folder named static in your app.

mkdir static

{% code title="index.html" %}

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Yew Sample App</title>
        <script type="module">
			import init from "./wasm.js"
			init()
		</script>
    </head>
    <body></body>
</html>

{% endcode %}

Run your App!

Using wasm-pack is the preferred way to get up and running. If you haven't already, install wasm-pack with cargo install wasm-pack and then build and start a development server by running:

wasm-pack build --target web --out-name wasm --out-dir ./static

wasm-pack generates a bundle in the ./static directory with your app's compiled WebAssembly along with a JavaScript wrapper which will load your application's WebAssembly binary and run it.

Then, use your favorite web server to server the files under ./static. For example:

cargo +nightly install miniserver
miniserve ./static --index index.html

Choose web-sys or stdweb

Introduction

Yew apps can be built with either web-sys or stdweb. These two crates provide the bindings between Rust and Web APIs. You'll need to choose one or the other when adding yew to your cargo dependencies:

{% code title="Cargo.toml" %}


#![allow(unused_variables)]
fn main() {
Choose `web-sys`
yew = "0.15"

Choose `stdweb`
yew = { version = "0.15", package = "yew-stdweb" }
}

{% endcode %}

We recommend using web-sys due to its support from the Rust / Wasm Working Group.

Example Usage


#![allow(unused_variables)]
fn main() {
// web-sys
let window: web_sys::Window = web_sys::window().expect("window not available");
window.alert_with_message("hello from wasm!").expect("alert failed");

// stdweb
let window: stdweb::web::Window = stdweb::web::window();
window.alert("hello from wasm!");

// stdweb with js! macro
use stdweb::js;
use stdweb::unstable::TryFrom;
use stdweb::web::Window;

let window_val: stdweb::Value = js!{ return window; }; // <- JS syntax inside!
let window = Window::try_from(window_val).expect("conversion to window failed");
window.alert("hello from wasm!");
}

The APIs for the two crates differ slightly but they serve roughly the same purpose with similar functionality.

Choosing One

There are a few different angles to consider when choosing between using web-sys and stdweb for your app. Note, it's possible to use both in one app, but to minimize the binary size of your compiled .wasm it's best to choose one or the other.

web-sysstdweb
Project StatusActively maintained by the Rust / Wasm Working GroupNo Github activity for over 4 months
Web API CoverageRust APIs are auto-generated from the Web IDL spec and so should have 100% coverage.Browser APIs are added as needed by the community
Rust API DesignTakes conservative approach by returning Result for most API callsOften avoids Result in favor of panics. For instance, stdweb::web::window() will panic when called in a worker.
Supported Build Tools
  • wasm-bindgen
  • wasm-pack
  • cargo-web
  • wasm-bindgen
  • wasm-pack
Supported Targets
  • wasm32-unknown-unknown
  • wasm32-unknown-unknown
  • wasm32-unknown-emscripten
  • asmjs-unknown-emscripten

Learn through examples

The Yew repository is chock-full of examples (in various states of maintenance). We recommend perusing them to get a feel for how to use different framework features. We also welcome pull-requests and issues for when they inevitably get neglected and need some ♥️


description: The procedural macro for generating HTML and SVG

Using html!

The html! macro allows you to write HTML and SVG code declaratively. It is similar to JSX (a Javascript extension which allows you to write HTML code inside of Javascript).

Important notes

  1. The html! macro only accepts one root html node (you can counteract this by using fragments or iterators)
  2. An empty html! {} invocation is valid and will not render anything
  3. Literals must always be quoted and wrapped in braces: html! { "Hello, World" }

{% page-ref page="lists.md" %}

{% page-ref page="elements.md" %}

{% page-ref page="literals-and-expressions.md" %}

{% page-ref page="components.md" %}

Lists

Fragments

The html! macro always requires a single root node. In order to get around this restriction, it's valid to wrap content in empty tags:

{% tabs %} {% tab title="Valid" %}


#![allow(unused_variables)]
fn main() {
html! {
    <>
        <div></div>
        <p></p>
    </>
}
}

{% endtab %}

{% tab title="Invalid" %}


#![allow(unused_variables)]
fn main() {
/* error: only one root html element allowed */

html! {
    <div></div>
    <p></p>
}
}

{% endtab %} {% endtabs %}

Iterators

Yew supports two different syntaxes for building html from an iterator:

{% tabs %} {% tab title="Syntax Type 1" %}


#![allow(unused_variables)]
fn main() {
html! {
    <ul class="item-list">
        { self.props.items.iter().map(renderItem).collect::<Html>() }
    </ul>
}
}

{% endtab %}

{% tab title="Syntax Type 2" %}


#![allow(unused_variables)]
fn main() {
html! {
    <ul class="item-list">
        { for self.props.items.iter().map(renderItem) }
    </ul>
}
}

{% endtab %} {% endtabs %}


description: Both HTML and SVG elements are supported

Elements

Tag Structure

Element tags must either self-close <... /> or have a corresponding close tag for each open tag

{% tabs %} {% tab title="Open - Close" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div id="my_div"></div>
}
}

{% endtab %}

{% tab title="INVALID" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div id="my_div"> // <- MISSING CLOSE TAG
}
}

{% endtab %}

{% tab title="Self-Closing" %}


#![allow(unused_variables)]
fn main() {
html! {
  <input id="my_input" />
}
}

{% endtab %}

{% tab title="INVALID" %}


#![allow(unused_variables)]
fn main() {
html! {
  <input id="my_input"> // <- MISSING SELF-CLOSE
}
}

{% endtab %} {% endtabs %}

{% hint style="info" %} For convenience, elements which usually require a closing tag are allowed to self-close. For example, writing html! { <div class="placeholder" /> } is valid. {% endhint %}

Children

Create complex nested HTML and SVG layouts with ease:

{% tabs %} {% tab title="HTML" %}


#![allow(unused_variables)]
fn main() {
html! {
    <div>
        <div data-key="abc"></div>
        <div class="parent">
            <span class="child" value="anything"></span>
            <label for="first-name">{ "First Name" }</label>
            <input type="text" id="first-name" value="placeholder" />
            <input type="checkbox" checked=true />
            <textarea value="write a story" />
            <select name="status">
                <option selected=true disabled=false value="">{ "Selected" }</option>
                <option selected=false disabled=true value="">{ "Unselected" }</option>
            </select>
        </div>
    </div>
}
}

{% endtab %}

{% tab title="SVG" %}


#![allow(unused_variables)]
fn main() {
html! {
    <svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
        <path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
        <g filter="url(#filter0_d)">
            <circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
            <circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
        </g>
        <circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
        <defs>
            <filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
                <feGaussianBlur stdDeviation="2"/>
                <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
            </filter>
        </defs>
    </svg>
}
}

{% endtab %} {% endtabs %}

Classes

There are a number of convenient options for specifying classes for an element:

{% tabs %} {% tab title="Literal" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class="container"></div>
}
}

{% endtab %}

{% tab title="Multiple" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class="container center-align"></div>
}
}

{% endtab %}

{% tab title="Interpolated" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class=format!("{}-container", size)></div>
}
}

{% endtab %}

{% tab title="Expression" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class=self.classes()></div>
}
}

{% endtab %}

{% tab title="Tuple" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class=("class-1", "class-2")></div>
}
}

{% endtab %}

{% tab title="Vector" %}


#![allow(unused_variables)]
fn main() {
html! {
  <div class=vec!["class-1", "class-2"]></div>
}
}

{% endtab %} {% endtabs %}

Listeners

Listener attributes need to be passed a Callback which is a wrapper around a closure. How you create your callback depends on how you wish your app to react to a listener event:

{% tabs %} {% tab title="Component Handler" %}


#![allow(unused_variables)]
fn main() {
struct MyComponent {
    link: ComponentLink<Self>,
}

enum Msg {
    Click,
}

impl Component for MyComponent {
    type Message = Msg;
    type Properties = ();

    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        MyComponent { link }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => {
                // Handle Click
            }
        }
    }

    fn view(&self) -> Html {
        // Create a callback from a component link to handle it in a component
        let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
        html! {
            <button onclick=click_callback>
                { "Click me!" }
            </button>
        }
    }
}
}

{% endtab %}

{% tab title="Agent Handler" %}


#![allow(unused_variables)]
fn main() {
struct MyComponent {
    worker: Dispatcher<MyWorker>,
}

impl Component for MyComponent {
    type Message = ();
    type Properties = ();

    fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
        MyComponent {
            worker: MyWorker::dispatcher()
        }
    }

    fn update(&mut self, _: Self::Message) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        // Create a callback from a worker to handle it in another context
        let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
        html! {
            <button onclick=click_callback>
                { "Click me!" }
            </button>
        }
    }
}
}

{% endtab %}

{% tab title="Other Cases" %}


#![allow(unused_variables)]
fn main() {
struct MyComponent;

impl Component for MyComponent {
    type Message = ();
    type Properties = ();

    fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
        MyComponent
    }

    fn update(&mut self, _: Self::Message) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        // Create an ephemeral callback
        let click_callback = Callback::from(|| {
            ConsoleService::new().log("clicked!");
        });

        html! {
            <button onclick=click_callback>
                { "Click me!" }
            </button>
        }
    }
}
}

{% endtab %} {% endtabs %}

Literals & Expressions

Literals

If expressions resolve to types that implement Display, they will be converted to strings and inserted into the DOM as a Text node.

All display text must be enclosed by {} blocks because text is handled like an expression. This is the largest deviation from normal HTML syntax that Yew makes.


#![allow(unused_variables)]
fn main() {
let text = "lorem ipsum";
html!{
    <>
        <div>{text}</div>
        <div>{"dolor sit"}</div>
        <span>{42}</span>
    </>
}
}

Expressions

You can insert expressions in your HTML using {} blocks, as long as they resolve to Html


#![allow(unused_variables)]
fn main() {
html! {
  <div>
    {
      if show_link {
        html! {
          <a href="https://example.com">{"Link"}</a>
        }
      } else {
        html! {}
      }
    }
  </div>
}
}

It often makes sense to extract these expressions into functions or closures to optimize for readability:


#![allow(unused_variables)]
fn main() {
let show_link = true;
let maybe_display_link = move || -> Html {
  if show_link {
    html! {
      <a href="https://example.com">{"Link"}</a>
    }
  } else {
    html! {}
  }
};

html! {
     <div>{maybe_display_link()}</div>
}
}

description: Introduction of Yew component

Components

What are Components

Components are the building blocks of Yew. They manage their own state and can render themselves to the DOM. Components are created by implementing the Component trait which describes the lifecycle of a component.

Example of component


#![allow(unused_variables)]
fn main() {
use yew::prelude::*;

pub struct ExampleComponent {
    // state of the component
    name: String,
    show_message: bool,

    // properties and events struct
    props: Props,

    // link field supports the mechanism through which components are able to register callbacks and update themselves
    link: ComponentLink<Self>,
}

// enum of "Messages" that will be used to mutate the component state
pub enum Msg {
    Click,
}

// definition of properties and events of the component
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or("Superman".to_string())]
    pub name: String,

    #[prop_or_default]
    pub onmyclickevent:Callback<String>,
}

impl Component for ExampleComponent {
    type Message = Msg;
    type Properties = Props;

    // Initialization of the state
    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            props: props.clone(),
            name: props.name.into(),
            show_message: false,
        }
    }

    // This method is executed each time the link.callbacks is called
    // you can mutate the state based on the message received
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {

            // mutate the component state
            Msg::Click => self.show_message = true,
        }
        true
    }

    // you can use change method to decide if you would like to re-render when properties change 
    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        if self.props != props {
            self.props = props;
            true
        } else {
            false
        }
    }

    // Rendering of the component
    fn view(&self) -> Html {
        // different rendering depend on the component state
        if !self.show_message {
            html! {
                // Listen to HTML events and trigger a message that will be managed in the update method
                <button onclick=self.link.callback( |_| Msg::Click )>{"Click to say hello!"}</button>
            }
        } else {
            html! {
                // Use state value in the html
                <h1>{format!("Hello {}", self.name)}</h1>
            }
        }
    }
}
}

description: A component can maintain its own state and render information depending on it

Internal state

The component can manage it's own state using a struct that implement the trait Component. The HTML rendering is based on this state. When the state change the component might be re-rendered.


#![allow(unused_variables)]
fn main() {
use yew::prelude::*;

pub struct InternalStateComponent {
    name:String,
}

impl Component for InternalStateComponent {
    type Message = ();
    type Properties = ();

    fn create(_props: Self::Properties, _link: ComponentLink<Self>) -> Self {
        Self {
            name: "Clark".into(),
        }
    }

    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
        false
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            <>
                <h1>{format!("Hello {}",self.name)}</h1>
            </>
        }
    }
}
}

Defining state

Here we add the name field to the struct


#![allow(unused_variables)]
fn main() {
// ...
pub struct InternalStateComponent {
    name:String,
}
// ...
}

State initialization

The component lifecycle will initialize the state in the create method.


#![allow(unused_variables)]
fn main() {
// ...
    fn create(_props: Self::Properties, _link: ComponentLink<Self>) -> Self {
        Self {
            name: "Clark".into(),
        }
    }
// ...
}

Rendering using the state

Using the html! macro we can render html using the state from the view method

please refer to the html! macro documentation page for more detail on how to render components as HTML


#![allow(unused_variables)]
fn main() {
// ...
    fn view(&self) -> Html {
        html! {
            <h1>{format!("Hello {}", self.name)}</h1>
        }
    }
// ...
}
}

description: Component could mutate internal state or re-render based on events emitted by html or Yew components

Listen to events and mutate state

The framework provide the capability to update the internal state, for example, when an event is emitted by a child component.

The update method could be called and mutate the internal state. The update method is called via self.link.callback, link being an attribute of the component struct.

The update method receives "context" by the argument msg of type Self::Message. You can define any type for Message. The common way is to define an enum Msg for any action that can mutate the state. Then define Msg as the type of Message in the Component trait implementation.

You can decide to render the component returning true from the Update method.


#![allow(unused_variables)]
fn main() {
use yew::prelude::*;

pub struct ListenEventComponent {
    link: ComponentLink<Self>,
    name: String,
    show_message: bool,
}

pub enum Msg {
    Click,
}

impl Component for ListenEventComponent {
    type Message = Msg;
    type Properties = ();

    fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            name: "Clark".into(),
            show_message: false,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => self.show_message = true,
        }
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        true
    }

    fn view(&self) -> Html {
        if !self.show_message {
            html! {
                <>
                    <button onclick=self.link.callback( |_| Msg::Click )>{"Click here!"}</button>
                </>
            }
        } else {
            html! {
                <>
                    <h1>{format!("Hello {}", self.name)}</h1>
                </>
            }
        }
    }
}

}

Define the link attribute in the state


#![allow(unused_variables)]
fn main() {
// ...
pub struct ListenEventComponent {
    link: ComponentLink<Self>,
// ...
}

Define a Message enum


#![allow(unused_variables)]

fn main() {
// ...
pub enum Msg {
    Click,
}

impl Component for ListenEventComponent {
    type Message = Msg;
    type Properties = ();

// ...
}

Update the internal state based on the context


#![allow(unused_variables)]
fn main() {
// ...
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => self.show_message = true,
        }
        true
    }
// ...
}

Register to the html events


#![allow(unused_variables)]

fn main() {
// ...
            html! {
                <button onclick=self.link.callback( |_| Msg::Click)>{"Click here!"}</button>
// ...
}

description: The component could expose property attributes to receive data from the parent html component

Properties

It’s good practice to divide up your application into multiple components and split them across different files. As your application becomes larger, this quickly becomes essential. For these components to be able to communicate with each other, components have properties – these are values which parent components pass to child components.

A component's properties should be defined using a separate struct which derives Properties and Clone traits.

The Properties trait requires that the Clone trait is implemented for all types for which Properties is derived.

It is common for this struct to be named Props

Properties may be defined as:

  • optional and initialized with Rust default value
  • optional and initialized with component default value
  • mandatory, the parent must define a value for the attribute

#![allow(unused_variables)]
fn main() {
use yew::prelude::*;

pub struct UseOfPropertyComponent {
    link: ComponentLink<Self>,
    props: Props,
    name: String,
    show_message: bool,
}

pub enum Msg {
    Click,
}

#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    pub name: String,
}

impl Component for UseOfPropertyComponent {
    type Message = Msg;
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            props: props.clone(),
            name: props.name.into(),
            show_message: false,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => self.show_message = true,
        }
        true
    }

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        if self.props != props {
            self.props = props;
            true
        } else {
            false
        }
    }

    fn view(&self) -> Html {
        if !self.show_message {
            html! {
                <button onclick=self.link.callback( |_| Msg::Click )>{"Click here!"}</button>
            }
        } else {
            html! {
                <h1>{format!("Hello {}",self.name)}</h1>
            }
        }
    }
}

}

In order to use this component you have to:

// ...
    <div class="full-height">
        {"In this example we pass the name as parameter of the Yew component."}
        <UseOfPropertyComponent name="Clark"/>
    </div>
// ...

Defining the properties struct


#![allow(unused_variables)]
fn main() {
// ...
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    pub name: String,
}
// ...

}

Attaching the properties to the state


#![allow(unused_variables)]
fn main() {
// ...
pub struct UseOfPropertyComponent {
    link: ComponentLink<Self>,
    props: Props,
    name: String,
    show_message: bool,
}
// ...
}

Initializing the properties


#![allow(unused_variables)]
fn main() {
// ...
    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            props: props.clone(),
            name: props.name.into(),
            show_message: false,
        }
    }
// ...
}

Here, to simply extend the previous example, we clone the value of the props argument. It may not be needed in your code

Defining property attributes

Optional property

Property can be defined optional just adding #[prop_or_default] on the property. In that case the property value will be initialized by the default Rust type value.


#![allow(unused_variables)]
fn main() {
// ...
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or_default]
    pub name: String,
}
// ...

}

In that case we will just say "Hello" ;-)

Optional property with component default value

A property can be defined as an optional property. In this case, it becomes necessary to define a default component value. Yew will automatically use this value if it is not provided when the component is initialized. Such properties should be annotated with the #[prop_or_(default_value)] attribute where default_value specifies the value which Yew should use.


#![allow(unused_variables)]
fn main() {
// ...
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or("Clark by default".to_string())]
    pub name: String,
}
// ...
}

In that case we will say "Hello Clark by default" ;-)

Mandatory property

If no attribute is defined the property will be "mandatory". So, if the property is omitted a compilation error is raised. The error looks like:

no method named build found for struct components::comp4::PropsBuilder<...PropsBuilderStep_missing_required_prop_name> in the current scope method not found in ...::PropsBuilder<...PropsBuilderStep_missing_required_prop_name>rustc(E0599) comp4.rs(14, 10): method build not found for this`

Optimizing rendering in the change method

In order to avoid unecessary rendering it's possible to compare the mutation of the props bag in the change method. This optimization imply to derive PartialEq for the Props struct to easily compare the props bag passed as argument of the method and the one in the internal state of the component.


#![allow(unused_variables)]
fn main() {
// ...
     fn change(&mut self, props: Self::Properties) -> ShouldRender {
         if self.props != props {
             self.props = props;
             true
         } else {
             false
         }
     }
// ...

}

Memory/speed overhead of using Properties

In Component::view, you take a reference to the component's state, and use that to create Html. Properties, however, are owned values. This means that in order to create them and pass them to child components, we need to take ownership of the references provided in the view function. This is done by implicitly cloning the references as they are passed to components in order to get owned values.

This means that each component has its own distinct copy of the state passed down from its parent, and that whenever you re-render a component, the props for all child components of the re-rendering component will have to be cloned.

The implication of this is if you would otherwise be passing huge amounts of data down as props (Strings that are 10s of kilobytes in size), you may want to consider turning your child component into a function which returns Html that the parent calls, as this means that data does not have to be cloned.

If you won't need to modify the data passed down through props you can wrap it in an Rc so that only a reference-counted pointer to the data is cloned, instead of the actual data itself.

Example


#![allow(unused_variables)]
fn main() {
use std::rc::Rc;
use yew::Properties;

#[derive(Clone, PartialEq)]
pub enum LinkColor {
    Blue,
    Red,
    Green,
    Black,
    Purple,
}

impl Default for LinkColor {
    fn default() -> Self {
        // The link color will be blue unless otherwise specified.
        LinkColor::Blue
    }
}

#[derive(Properties, Clone, PartialEq)]
pub struct LinkProps {
    /// The link must have a target.
    href: String,
    /// If the link text is huge, this will make copying the string much cheaper.
    /// This isn't usually recommended unless performance is known to be a problem.
    text: Rc<String>,
    /// Color of the link.
    #[prop_or_default]
    color: LinkColor,
    /// The view function will not specify a size if this is None.
    #[prop_or_default]
    size: Option<u32>,
    /// When the view function doesn't specify active, it defaults to true.
    #[prop_or(true)]
    active: bool,
}
}

description: Component could share update to the parent html component emitting events

Emit events

It's possible to update the parent html component emitting events in the same way we received data via properties.

The events are defined as part of the Properties struct.

When it's needed you just need to call the emit() method of the event, to propagate the update to the parents containers.

You can name the event however you want to. It's usually started by "on"


#![allow(unused_variables)]
fn main() {
use yew::prelude::*;

pub struct EmitEventComponent {
    link: ComponentLink<Self>,
    props: Props,
    name: String,
    show_message: bool,
}

pub enum Msg {
    Click,
    Click4Event,
}

#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or("Clark by default".to_string())]
    pub name: String,

    #[prop_or_default]
    pub onmyclickevent:Callback<String>,
}

impl Component for EmitEventComponent {
    type Message = Msg;
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            props: props.clone(),
            name: props.name.into(),
            show_message: false,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => self.show_message = true,
            Msg::Click4Event => self.props.onmyclickevent.emit("Hello Loise".into()),
        }
        true
    }

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        if self.props != props {
            self.props = props;
            true
        } else {
            false
        }
    }

    fn view(&self) -> Html {
        if !self.show_message {
            html! {
                <button onclick=self.link.callback( |_| Msg::Click )>{"Click here!"}</button>
            }
        } else {
            html! {
                <>
                    {"Click on clark to raised an event for the parent container ;-)"}
                    <h1 onclick=self.link.callback( |_| Msg::Click4Event) >
                    {format!("Hello {}",self.name)}</h1>
                </>
            }
        }
    }
}
}

Define the Event

Here we are defining a new event named onmyclickevent with a String as parameter.


#![allow(unused_variables)]
fn main() {
// ...
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or("Clark by default".to_string())]
    pub name: String,
    #[prop_or_default]
    pub onmyclickevent:Callback<String>,
}
// ...
}

Emit the Event

The event are usually emitted in the update() method as effect of message


#![allow(unused_variables)]
fn main() {
// ...
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Click => self.show_message = true,
            Msg::Click4Event => self.props.onmyclickevent.emit("Hello Loise".into()),
        }
        true
    }
// ...
}

Mandatory or optionally binded events

Like for properties you can define the events to be be mandatory or optionaly binded in the parent component.

This is defined providing or not attributes to the event definition.


#![allow(unused_variables)]
fn main() {
// ...
#[derive(Properties, Clone, PartialEq)]
pub struct Props{
    #[prop_or("Clark by default".to_string())]
    pub name: String,

    pub on_i_am_mandatory_event:Callback<()>,

    #[prop_or_default]
    pub on_i_am_optional_event:Callback<()>,
}
// ...
}

Like for the properties a compilation error will be raised in case you omitted to bind a mandatory event. The message could be something like:

no method named build found for struct components::comp4::PropsBuilder<...PropsBuilderStep_missing_required_prop_name> in the current scope method not found in ...::PropsBuilder<...PropsBuilderStep_missing_required_prop_name>rustc(E0599) comp4.rs(14, 10): method build not found for this`

Nested components


description: Create complex layouts with component hierarchies

Components

Basic

Any type that implements Component can be used in the html! macro:


#![allow(unused_variables)]
fn main() {
html!{
    <>
        // No properties
        <MyComponent />

        // With Properties
        <MyComponent prop1="lorem" prop2="ipsum" />

        // With the whole set of props provided at once
        <MyComponent with props />
    </>
}
}

Nested

Components can be passed children if they have a children field in their Properties.

{% code title="parent.rs" %}


#![allow(unused_variables)]
fn main() {
html! {
    <Container>
        <h4>{ "Hi" }</h4>
        <div>{ "Hello" }</div>
    </Container>
}
}

{% endcode %}

{% code title="container.rs" %}


#![allow(unused_variables)]
fn main() {
pub struct Container(Props);

#[derive(Properties)]
pub struct Props {
    pub children: Children,
}

impl Component for Container {
    type Properties = Props;

    // ...

    fn view(&self) -> Html {
       html! {
           <div id="container">
               { self.0.children.render() }
           </div>
       }
    }
}
}

{% endcode %}

Nested Children with Props

Nested component properties can be accessed and mutated if the containing component types its children. In the following example, the List component can wrap ListItem components. For a real world example of this pattern, check out the yew-router source code. For a more advanced example, check out the nested-list example in the main yew repository.

{% code title="parent.rs" %}


#![allow(unused_variables)]
fn main() {
html! {
    <List>
        <ListItem value="a" />
        <ListItem value="b" />
        <ListItem value="c" />
    </List>
}
}

{% endcode %}

{% code title="list.rs" %}


#![allow(unused_variables)]
fn main() {
pub struct List(Props);

#[derive(Properties)]
pub struct Props {
    pub children: ChildrenWithProps<ListItem>,
}

impl Component for List {
    type Properties = Props;

    // ...

    fn view(&self) -> Html {
        html!{{
            for self.0.children.iter().map(|mut item| {
                item.props.value = format!("item-{}", item.props.value);
                item
            })
        }}
    }
}
}

{% endcode %}


description: Out-of-band DOM access

Refs

Refs

The ref keyword can be used inside of any HTML element or component to get the DOM Element that the item is attached to. This can be used to make changes to the DOM outside of the view lifecycle method.

This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.

The syntax is:


#![allow(unused_variables)]
fn main() {
// In create
self.node_ref = NodeRef::default();

// In view
html! {
    <div ref=self.node_ref.clone()></div>
}

// In update
let has_attributes = self.node_ref.try_into::<Element>().has_attributes();
}

Styling

<Work in progress>

A proposal for proper CSS support can be found here: https://github.com/yewstack/yew/issues/533

In the meantime you can define the style with any standard CSS tools.


description: Component life cycle

Life cycle

{% hint style="info" %} Contribute to our docs: Add a diagram of the component lifecycle {% endhint %}

Lifecycle Methods

Create

When a component is created, it receives properties from its parent component as well as a ComponentLink. The properties can be used to initialize the component's state and the "link" can be used to register callbacks or send messages to the component.

It is common to store the props and the link in your component struct, like so:


#![allow(unused_variables)]
fn main() {
pub struct MyComponent {
    props: Props,
    link: ComponentLink<Self>,
}

impl Component for MyComponent {
    type Properties = Props;
    // ...

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        MyComponent { props, link }
    }

    // ...
}
}

View

Components declare their layout in the view() method. Yew provides the html! macro for declaring HTML and SVG nodes and their listeners as well as child components. The macro acts a lot like React's JSX, but uses Rust expressions instead of JavaScript.


#![allow(unused_variables)]
fn main() {
impl Component for MyComponent {
    // ...

    fn view(&self) -> Html {
        let onclick = self.link.callback(|_| Msg::Click);
        html! {
            <button onclick=onclick></button>
        }
    }
}
}

For usage details, check out the html! guide:

{% page-ref page="../html/" %}

Rendered

The rendered() component lifecycle method is called after view() is processed and Yew has rendered your component, but before the browser refreshes the page. A component may wish to implement this method to perform actions that can only be done after the component has rendered elements. You can check whether this is the first time the component was rendered via the first_render parameter.


#![allow(unused_variables)]
fn main() {
use stdweb::web::html_element::InputElement;
use stdweb::web::IHtmlElement;
use yew::prelude::*;

pub struct MyComponent {
    node_ref: NodeRef,
}

impl Component for MyComponent {
    // ...

    fn view(&self) -> Html {
        html! {
            <input ref=self.node_ref.clone() type="text" />
        }
    }

    fn rendered(&mut self, first_render: bool) {
        if first_render {
            if let Some(input) = self.node_ref.try_into::<InputElement>() {
                input.focus();
            }
        }
    }
}
}

{% hint style="info" %} Note that this lifecycle method does not require an implementation and will do nothing by default {% endhint %}

Update

Components are dynamic and can register to receive asynchronous messages. The update() lifecycle method is called for each message. This allows the component to update itself based on what the message was, and determine if it needs to re-render itself. Messages can be triggered by HTML elements listeners or be sent by child components, Agents, Services, or Futures.

Here's an example of what update() could look like:


#![allow(unused_variables)]
fn main() {
pub enum Msg {
    SetInputEnabled(bool)
}

impl Component for MyComponent {
    type Message = Msg;

    // ...

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
       match msg {
           Msg::SetInputEnabled(enabled) => {
               if self.input_enabled != enabled {
                   self.input_enabled = enabled;
                   true // Re-render
               } else {
                   false
               }
           }
       }
    }
}
}

Change

Components may be re-rendered by their parents. When this happens, they could receive new properties and choose to re-render. This design facilitates parent to child component communication through changed properties. You don't have to implement change() but you probably want to if you want to update a component via props after it has been created.

A naive implementation would look like:


#![allow(unused_variables)]
fn main() {
impl Component for MyComponent {
    // ...

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
       self.props = props;
       true // This will always re-render when new props are provided.
    }
}
}

Destroy

After Components are unmounted from the DOM, Yew calls the destroy() lifecycle method to support any necessary clean up operations. This method is optional and does nothing by default.

Associated Types

The Component trait has two associated types: Message and Properties.


#![allow(unused_variables)]
fn main() {
impl Component for MyComponent {
    type Message = Msg;
    type Properties = Props;

    // ...
}
}

Message represents a variety of messages that can be processed by the component to trigger some side effect. For example, you may have a Click message which triggers an API request or toggles the appearance of a UI component. It is common practice to create an enum called Msg in your component's module and use that as the message type in the component. It is common to shorten "message" to "msg".


#![allow(unused_variables)]
fn main() {
enum Msg {
    Click,
}
}

Properties represents the information passed to a component from its parent. This type must implements the Properties trait (usually by deriving it) and can specify whether certain properties are required or optional. This type is used when creating and updating a component. It is common practice to create a struct called Props in your component's module and use that as the component's Properties type. It is common to shorten "properties" to "props". Since props are handed down from parent components, the root component of your application typically has a Properties type of (). If you wish to specify properties for your root component, use the App::mount_with_props method.


description: ComponentLink and Callbacks.

Callbacks

The component "link" is the mechanism through which components are able to register callbacks and update themselves.

ComponentLink API

callback

Registers a callback that will send a message to the component's update mechanism when it is executed. Under the hood, it will call send_self with the message that is returned by the provided closure. A Fn(IN) -> Vec<COMP::Message> is provided and a Callback<IN> is returned.

send_message

Sends a message to the component immediately after the current loop finishes, causing another update loop to initiate.

send_message_batch

Registers a callback that sends a batch of many messages at once when it is executed. If any of the messages cause the component to re-render, the component will re-render after all messages in the batch have been processed. A Fn(IN) -> COMP::Message is provided and a Callback<IN> is returned.

Callbacks

(This might need its own short page.)

Callbacks are used to communicate with services, agents, and parent components within Yew. They are just a Fn wrapped by an Rc to allow them to be cloned.

They have an emit function that takes their <IN> type as an argument and converts that to a message expected by its destination. If a callback from a parent is provided in props to a child component, the child can call emit on the callback in its update lifecycle hook to send a message back to its parent. Closures or Functions provided as props inside the html! macro are automatically converted to Callbacks.


description: Yew's Actor System

Agents

Agents are similar to Angular's Services (but without dependency injection), and provide a Yew with an Actor Model. Agents can be used to route messages between components independently of where they sit in the component hierarchy, or they can be used to create a global state, and they can also be used to offload computationally expensive tasks from the main thread which renders the UI. There is also planned support for using agents to allow Yew applications to communicate accross tabs (in the future).

In order for agents to run concurrently, Yew uses web-workers.

Lifecycle

Agent lifecycle

Types of Agents

Reaches

  • Job - Spawn a new agent on the UI thread for every new bridge. This is good for moving shared but independent behavior that communicates with the browser out of components. (TODO verify) When the task is done, the agent will disappear.
  • Context - Bridges will spawn or connect to an agent on the UI thread. This can be used to coordinate with state between components or other agents. When no bridges are connected to this agent, the agent will disappear.
  • Private - Same as Job, but runs on its own web worker.
  • Public - Same as Context, but runs on its own web worker.
  • Global (WIP)

Communication between Agents and Components

Bridges

A bridge allows bi-directional communication between an agent and a component. Bridges also allow agents to communicate with one another.

Dispatchers

A dispatcher allows uni-directional communication between a component and an agent. A bridge allows a component to send messages to an agent.

Overhead

Agents communicate by serializing their messages using bincode. So there is a higher performance cost than just calling functions. Unless the cost of computation or the need to coordinate across arbitrary components will outweigh the cost of message passing, you should contain your logic to functions where possible.

Further reading

  • The pub_sub example shows how components can use agents to communicate with each other.

description: Yew's glue to browser APIs.

Services

Format

{% hint style="info" %} Contribute to our docs: Explain the format module in depth {% endhint %}


description: Yew's official router

Router

https://crates.io/crates/yew-router

Routers in Single Page Applications (SPA) handle displaying different pages depending on what the URL is. Instead of the default behavior of requesting a different remote resource when a link is clicked, the router instead sets the URL locally to point to a valid route in your application. The router then detects this change and then decides what to render.

Core Elements

Route

Contains a String representing everything after the domain in the url and optionally the state stored in the history api.

RouteService

Communicates with the browser to get and set Routes.

RouteAgent

Owns a RouteService and is used to coordinate updates when the route changes, either from within the application logic or from an event fired from the browser.

Switch

The Switch trait is used to convert a Route to and from the implementer of this trait.

Router

The Router component communicates with RouteAgent and will automatically resolve Routes it gets from the agent into switches, which it will expose via a render prop that allows specifying how the resulting switch gets converted to Html.

How to use the Router

First, you want to create a type that represents all the states of your application. Do note that while this typically is an enum, structs are supported as well, and that you can nest other items that implement Switch inside.

Then you should derive Switch for your type. For enums, every variant must be annotated with #[to = "/some/route"], or if you use a struct instead, that must appear outside the struct declaration.


#![allow(unused_variables)]
fn main() {
#[derive(Switch)]
enum AppRoute {
  #[to="/login"]
  Login,
  #[to="/register"]
  Register,
  #[to="/delete_account"]
  Delete, 
  #[to="/posts/{id}"]
  ViewPost(i32),
  #[to="/posts/view"]
  ViewPosts,
  #[to="/"]
  Home
}
}

Do note that the implementation generated by the derive macro for Switch will try to match each variant in order from first to last, so if any route could possibly match two of your specified to annotations, then the first one will match, and the second will never be tried. For example, if you defined the following Switch, the only route that would be matched would be AppRoute::Home.


#![allow(unused_variables)]
fn main() {
#[derive(Switch)]
enum AppRoute {
  #[to="/"]
  Home,
  #[to="/login"]
  Login,
  #[to="/register"]
  Register,
  #[to="/delete_account"]
  Delete, 
  #[to="/posts/{id}"]
  ViewPost(i32),
  #[to="/posts/view"]
  ViewPosts,
}
}

You can also capture sections using variations of {} within your #[to = ""] annotation. {} means capture text until the next separator (either "/", "?", "&", or "#" depending on the context). {*} means capture text until the following characters match, or if no characters are present, it will match anything. {<number>} means capture text until the specified number of separators are encountered (example: {2} will capture until two separators are encountered).

For structs and enums with named fields, you must specify the field's name within the capture group like so: {user_name} or {*:age}.

The Switch trait works with capture groups that are more structured than just Strings. You can specify any type that implements Switch. So you can specify that the capture group is a usize, and if the captured section of the URL can't be converted to it, then the variant won't match.


description: Make your app faster.

Optimizations & Best Practices

neq_assign

When a component receives props from its parent component, the change method is called. This, in addition to allowing you to update the component's state, also allows you to return a ShouldRender boolean value that indicates if the component should re-render itself in response to the prop changes.

Re-rendering is expensive, and if you can avoid it, you should. As a general rule, you only want to re-render when the props actually changed. The following block of code represents this rule, returning true if the props differed from the previous props:


#![allow(unused_variables)]
fn main() {
use yew::ShouldRender;

#[derive(PartialEq)]
struct ExampleProps;

struct Example {
    props: ExampleProps,
};

impl Example {
    fn change(&mut self, props: ExampleProps) -> ShouldRender {
        if self.props != props {
            self.props = props;
            true
        } else {
            false
        }
    }
}
}

But we can go further! This is six lines of boilerplate can be reduced down to one by using a trait and a blanket implementation for anything that implements PartialEq. Check out the yewtil crate's NeqAssign trait here.

RC

In an effort to avoid cloning large chunks of data to create props when re-rendering, we can use smart pointers to only clone the pointer instead. If you use Rc<_>s in your props and child components instead of plain unboxed values, you can delay cloning until you need to modify the data in the child component, where you use Rc::make_mut to clone and get a mutable reference to the data you want to alter. By not cloning until mutation, child components can reject props identical to their state-owned props in Component::change for almost no performance cost, versus the case where the data itself needs to be copied into the props struct in the parent before it is compared and rejected in the child.

This optimization is most useful for data types that aren't Copy. If you can copy your data easily, then it probably isn't worth putting it behind a smart pointer. For structures that can contain lots of data like Vec, HashMap, and String, this optimization should be worthwhile.

This optimization works best if the values are never updated by the children, and even better, if they are rarely updated by parents. This makes Rc<_>s a good choice for wrapping property values in for pure components.

View Functions

For code readability reasons, it often makes sense to migrate sections of html! to their own functions so you can avoid the rightward drift present in deeply nested HTML.

Pure Components/Function Components

Pure components are components that don't mutate their state, only displaying content and propagating messages up to normal, mutable components. They differ from view functions in that they can be used from within the html! macro using the component syntax (<SomePureComponent />) instead of expression syntax ({some_view_function()}), and that depending on their implementation, they can be memoized - preventing re-renders for identical props using aforementioned neq_assign logic.

Yew doesn't natively support pure or function components, but they are available via external crates.

Function components don't exist yet, but in theory, pure components could be generated by using proc macros and annotating functions.

Keyed DOM nodes when they arrive

Compile speed optimizations using Cargo Workspaces

Arguabley, the largest drawback to using Yew is the long time it takes to compile. Compile time seems to correlate with the quantity of code found within html! macro blocks. This tends to not be a significant problem for smaller projects, but for webapps that span multiple pages, it makes sense to break apart your code across multiple crates to minimize the amount of work the compiler has to do.

You should try to make your main crate handle routing/page selection, move all commonly shared code to another crate, and then make a different crate for each page, where each page could be a different component, or just a big function that produces Html. In the best case scenario, you go from rebuilding all of your code on each compile to rebuilding only the main crate, and one of your page crates. In the worst case, where you edit something in the "common" crate, you will be right back to where you started: compiling all code that depends on that commonly shared crate, which is probably everything else.

If your main crate is too heavyweight, or you want to rapidly iterate on a deeply nested page (eg. a page that renders on top of another page), you can use an example crate to create a more simple implementation of the main page and render your work-in-progress component on top of that.

Build size optimization

  • optimize Rust code
    • wee_aloc ( using tiny allocator )
    • cargo.toml ( defining release profile )
  • optimize wasm code using wasm-opt

More information about code size profiling: rustwasm book

wee_alloc

wee_alloc is a tiny allocator that is much smaller than the allocator that is normally used in Rust binaries. Replacing the default allocator with this one will result in smaller WASM file sizes, at the expense of speed and memory overhead.

The slower speed and memory overhead are minor in comparison to the size gains made by not including the default allocator. This smaller file size means that your page will load faster, and so it is generally recommended that you use this allocator over the default, unless your app is doing some allocation-heavy work.


#![allow(unused_variables)]
fn main() {
// Use `wee_alloc` as the global allocator.
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}

Cargo.toml

It is possible to setup release build for smaller size using [profile.release] section in Cargo.toml

Rust profiles documentation

[profile.release]
# less code to include into binary
panic = 'abort' 
# optimization over all codebase ( better optimization, slower build )
codegen-units = 1
# optimization for size ( more aggresive )
opt-level = 'z' 
# optimization for size 
# opt-level = 's' 
# link time optimization using using whole-program analysis
lto = true

wasm-opt

Further more it is possible to optimize size of wasm code.

wasm-opt info: binaryen project

The Rust Wasm book features a section about reducing the size of WASM binaries: Shrinking .wasm size

  • using wasm-pack which by default optimizes wasm code in release builds
  • using wasm-opt directly on wasm files.
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm

Build size of 'minimal' example in yew/examples/

Note: wasm-pack combines optimization for Rust and wasm code. wasm-bindgen is in this example without any Rust size optimization.

used toolsize
wasm-bindgen158KB
wasm-binggen + wasm-opt -Os116KB
wasm-pack99 KB

description: Low level details about the framework

Low-level library internals

Component-lifecycle state machine, vdom diff algorithm.


description: The planned feature roadmap for the Yew framework

Roadmap

Prioritization

The prioritization of upcoming features and focuses of the framework are determined by the community. In Spring 2020, a developer survey was sent out to collect feedback on the direction of the project. You can find the summary in the Yew Wiki.

{% hint style="info" %} Status of all major initiatives can be tracked on the Yew Github Project board {% endhint %}

Focuses

  1. Top Requested Features
  2. Production Readiness
  3. Documentation
  4. Pain Points

Top Requested Features

  1. Functional Components
  2. Component Library
  3. Better state management
  4. Server side rendering

Production Readiness

Documentation

  • Create tutorial
  • Simplify project setup

Pain Points


description: Testing your app

Testing

<TODO>

wasm_bingen_test

The Rust WASM working group maintains a crate called wasm_bindgen_test which allows you to run tests in a browser in similar fashion to how the built-in #[test] procedural macro works. More information is given in the Rust WASM working group's documentation for this module.

Debugging

Panics

Please use the console_error_panic crate for nicer stacktraces with Rust symbols. Note, that it is not compatible with apps built with cargo-web.

Console Logging

In general, Wasm web apps are able to interact with Browser APIs, and the console.log api is no exception. There are a few options available:

wasm-logger

This crate integrates with the familiar Rust log crate:

// setup
fn main() {
    wasm_logger::init(wasm_logger::Config::default());
}

// usage
log::info!("Update: {:?}", msg);

``ConsoleService``

This service is included within yew and is available when the "services" feature is enabled:


#![allow(unused_variables)]
fn main() {
// usage
ConsoleService::new()::info(format!("Update: {:?}", msg));
}

Source Maps

There is currently no first-class support for source maps for Rust / Wasm web apps. This, of course, is subject to change. If this is no longer true or if progress is made, please suggest a change!

Latest Info

[Dec 2019] Chrome DevTools update

There is still quite a bit of work to do though. For example, on the tooling side, Emscripten (Binaryen) and wasm-pack (wasm-bindgen) don’t support updating DWARF information on transformations they perform yet.

[2020] Rust Wasm debugging guide

Unfortunately, the debugging story for WebAssembly is still immature. On most Unix systems, DWARF is used to encode the information that a debugger needs to provide source-level inspection of a running program. There is an alternative format that encodes similar information on Windows. Currently, there is no equivalent for WebAssembly.

[2019] Rust Wasm roadmap

Debugging is tricky because much of the story is out of this working group's hands, and depends on both the WebAssembly standardization bodies and the folks implementing browser developer tools instead.


description: Libraries that can help with yew development

External Libs

Yewtil

Yewtil is a collection of common utilities that help you write Yew programs. It includes:

  • NeqAssign - As discussed earlier, is the best way to assign props to ensure minimal re-rendering.
  • PureComponents - Components that don't update any of their state. Using NeqAssign under the hood, they act as memoized functions that are called from inside the html! macro like normal components are.
  • Lrc - linked list reference counted smart pointer functions like Rc does, but allows for novel data update patterns.
  • Mrc/Irc - Mutable/Immutable reference counted smart pointers that function like Rc but are more ergonomic to use within Yew, due to implementing DerefMut and BorrowMutfor Mrc. This allows Mrc to be used with NeqAssign. Irc acts as an immutable view into the data, which makes this ideal for holding data used in display-only tasks.
  • History - A history tracking wrapper that uses a VecDeque to hold on to previous values that it has represented.
  • Futures - Support for running futures that send messages to component update loops.
  • Fetch - Abstractions for handling fetch requests made using web_sys and the aforementioned futures feature.

Looking For

Libraries that the ecosystem needs, but doesn't have yet.

Bootstrap/MaterialUi/arbitrary css framework component wrappers.