Exploring Yew, the rust-based frontend framework as a Vue Developer
Tags: rust, vue, javascript, webassembly
Published: 2022-09-27 19:11:05

---
title: Exploring Yew, the rust-based frontend framework as a Vue Developer
published: true
description: Learn how to build a web application using open API data from DummyJSON and Yew as a Vue developer.
tags: #rust #vue #javascript #webAssembly
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q9or9tlzw5k938llxs2z.png
# Use a ratio of 100:42 for best results.
---

WebAssembly, popularly known as WASM, has revolutionized how web applications are built. It has allowed developers to use their favourite programming languages to build web applications. 

With these possibilities, developers are not tasked with the burden of learning a JavaScript-based framework when building a frontend application. They can leverage their favourite programming language features like static typing, pattern matching, memory safety, e.t.c, to build frontend applications.

Yew is a modern Rust-based framework for building frontend applications using WebAssembly.

In this post, we will learn how to build a web application using open API data from [DummyJSON](https://dummyjson.com/) and Yew as a Vue developer.
GitHub repository can be found [here](https://github.com/Mr-Malomz/yew-starter).

## Similarities between Vue and Yew

The following are the similarities between the two technologies:

| **Features and Functionality**                                                         | **Vue** | **Yew**                                      |
| -------------------------------------------------------------------------------------- | ------- | -------------------------------------------- |
| Component-based structure                                                              | YES     | YES                                          |
| Minimizing DOM API call to improve performance                                         | YES     | YES                                          |
| JavaScript support                                                                     | YES     | YES                                          |
| Collection of reusable packages via NPM                                                | YES     | YES (Yew can leverage existing NPM packages) |
| Hot reloading support (seeing changes in real-time without restart) during development | YES     | YES                                          |



## Prerequisites

To fully grasp the concepts presented in this tutorial, the following requirements apply:

- Basic understanding of Vue
- Basic understanding of Rust
- Rust [installation](https://www.rust-lang.org/tools/install) 

## Development Environment Setup

First, we need to ensure the latest version of Rust is installed on our machine. We can upgrade to the stable version by running the command below:
```bash
rustup update
```

Next, we need to install a WASM target, a tool that helps us compile our Rust source code into browser-based WebAssembly and makes it run on a web browser. We can install it by running the command below:

```bash
rustup target add wasm32-unknown-unknown
```

Finally, we need to install Trunk, a tool for managing and packaging WebAssembly applications. We can do this by running the command below:

```bash
cargo install trunk
```

## Getting Started

To get started, we need to navigate to the desired directory and run the command below in our terminal:

```bash
cargo new yew-starter && cd yew-starter
```

This command creates a Rust project called `yew-starter` and navigates into the project directory.

`cargo` is Rust’s package manager. It works similarly to `npm` in the Vue ecosystem.

On running the command, `cargo` will generate a project directory with basic files as shown below:

![project directory](https://paper-attachments.dropbox.com/s_9BCE1CBC2B0480722FD813FD929E44A5064E5E6F163358D952B6D0F1E44E6335_1663963929277_Screenshot+2022-09-23+at+21.11.28.png)


`main.rs` is the entry point of our application.

`Cargo.toml` is the manifest file for specifying project metadata like packages, version, e.t.c. It works similarly to `package.json` in a Vue application.

Next, we proceed to install the required dependencies by modifying the `[dependencies]` section of the `Cargo.toml` file as shown below:

```toml
//other code section goes here

[dependencies]
yew = "0.19"
serde = "1.0.144"
gloo-net = "0.2"
wasm-bindgen-futures = "0.4"
```

`yew = "0.19"` is a Rust-based frontend framework

`serde = "1.0.136"` is a framework for serializing and deserializing Rust data structures. E.g. convert Rust structs to a JSON.

`gloo-net= "0.2"` is a HTTP requests library. It works similarly to `axios` in Vue ecosystem. 

`wasm-bindgen-futures = "0.4"` is a Rust-based library for performing asynchronous programming in Yew by bridging the gap between Rust asynchronous programming (futures) and JavaScript `Promises`. Basically, It helps leverage `Promise`-based web APIs in Rust.

We need to run the command below to install the dependencies:

```bash
cargo build
```

## Application Entry Point

With the project dependencies installed, we need to modify the `main.rs` file inside the `src` folder as shown below:

```rust
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    html! {
        <h1 class="text-primary">{ "Yew for Vue developers" }</h1>
    }
}

fn main() {
    yew::start_app::<App>();
}
```

Oops! It looks like a lot is going on in the snippet above. Let's break it down a bit.

`use yew::prelude::*` : Imports the required `yew` dependency and its associates by specifying `*`

`#[function_component(App)]`: Declares the `app` function as a Functional component with `App` as the name. The syntax used here is called Rust macros; macros are code that writes other codes.

`fn app() -> Html {…..}` : Uses the `html!` macro to create `Yew for Vue developers` markup. The macro works similarly to template syntax in Vue.

`yew::start_app::<App>()`: Starts a Yew application by mounting the `App`  component to the body of the document. It works similarly to `createApp(App).mount()` function in Vue.

**HTML Render**
Similarly to the way Vue renders into the DOM, the same principles apply in Yew. Next, we need to create an `index.html` file with Bootstrap CDN support in the root directory of our project and add the snippet below:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
    <title>Yew Starter</title>
</head>
<body>
</body>
</html>
```


![index file](https://paper-attachments.dropbox.com/s_9BCE1CBC2B0480722FD813FD929E44A5064E5E6F163358D952B6D0F1E44E6335_1663968287288_Screenshot+2022-09-23+at+22.24.27.png)


Next, we can test our application by starting the development server by running the command below in our terminal:

```bash
trunk serve --open
```

![Running application](https://paper-attachments.dropbox.com/s_31EFBE89767DCBAECA17886C39E9EDD4DAE74A27400AACEA2040F85D20DC7B09_1664015466473_Screenshot+2022-09-24+at+11.30.47.png)



## Building a real application with Yew

Now that we have a good grasp of how Yew works, we can proceed to build an application that integrates [DummyJSON](https://dummyjson.com/)’s user API.

**Module system in Rust**
In Vue, components forms the building block of an application. In our application, we will use Rust Module system to structure our application. 

To do this, we need to navigate to the `src` folder and create the `component` and `model` folder with their corresponding `mod.rs` file to manage visibility.

![Modules](https://paper-attachments.dropbox.com/s_9BCE1CBC2B0480722FD813FD929E44A5064E5E6F163358D952B6D0F1E44E6335_1663969904615_Screenshot+2022-09-23+at+22.50.44.png)


To use the code in the modules, we need to declare them as a module and import them into the `main.rs` file as shown below:

```rust
use yew::prelude::*;

//add below
mod components;
mod models;

#[function_component(App)]
fn app() -> Html {
  //app code goes here
}

fn main() {
    yew::start_app::<App>();
}
```

**Creating Models**
With that done, we need to create models to represent the response returned from the API. To do this, we need to navigate to the `models` folder, here, create a `user.rs` file and add the snippet below:

```rust
use serde::Deserialize;

#[derive(Clone, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct User {
    pub first_name: String,
    pub last_name: String,
    pub email: String,
    pub gender: String,
    pub phone: String,
}

#[derive(Clone, Deserialize, PartialEq)]
pub struct Users {
    pub users: Vec<User>
}
```

The snippet above does the following:

- Imports the required dependency
- Uses the `derive` macro to generate implementation support for formatting the output and deserializing the data structure. The `#[serde(rename_all = "camelCase")]` macro converts snake case properties to camel case (The API returns data in camel case)
- Creates a `User` struct with required properties needed from the API nested response
- Creates a `Users` struct with a `users` property; an array type of `User` struct. Dynamic arrays in Rust are represented as a vector

Sample of API response from DummyJSON below:

```json
{
  "users": [
    {
      "id": 1,
      "firstName": "Terry",
      "lastName": "Medhurst",
      "maidenName": "Smitham",
      "age": 50,
      "gender": "male",
      "email": "atuny0@sohu.com",
      ......
    },
      {...},
      {...}
    ],
}
```

**PS**: *The* `pub` *modifier makes the struct and its property public and can be accessed from other files/modules.*

Next, we must register the `user.rs` file as part of the `models` module. To do this, open the `mod.rs` in the `models` folder and add the snippet below:

```rust
pub mod user;
```

**Creating Components**
With the models fully setup, we can start creating our application building blocks.

First, we need to navigate to the `components` folder and create a `header.rs` file and add the snippet below:

```rust
use yew::prelude::*;

#[function_component(Header)]
pub fn header() -> Html {
    html! {
    <nav class="navbar bg-black">
        <div class="container-fluid">
            <a class="navbar-brand text-white" href="#">{"User List"}</a>
        </div>
    </nav>
    }
}
```

The snippet above creates a `Header` component to represent our application header.

Secondly, we need to create a `loader.rs` file in the same `components` folder and add the snippet below:

```rust
use yew::prelude::*;

#[function_component(Loader)]
pub fn loader() -> Html {
    html! {
    <div class="spinner-border" role="status">
        <span class="visually-hidden">{"Loading..."}</span>
    </div>
    }
}
```

The snippet above creates a `Loader` component representing a UI when our application is loading.

Thirdly, we need to create a `message.rs` file in the same `components` folders and add the snippet below:

```rust
use yew::prelude::*;

#[derive(Properties, PartialEq)]
pub struct MessageProp {
    pub text: String,
    pub css_class: String,
}

#[function_component(Message)]
pub fn message(MessageProp { text, css_class }: &MessageProp) -> Html {
    html! {
        <p class={css_class.clone()}>
            {text.clone()}
        </p>
    }
}
```

The snippet above does the following:

- Imports the required dependency
- Creates a `MessageProp` struct with `text` and `css_class` properties to represent the component property. The `#[derive(Properties, PartialEq)]` macros marks the struct as a component prop similar to a Vue application
- Destructures the props and use them as CSS class and display text in the markup

Fourthly, we need to create a `card.rs` file in the same `components` folders and add the snippet below:

```rust
use yew::prelude::*;
use crate::models::user::User;

#[derive(Properties, PartialEq)]
pub struct CardProp {
    pub user: User,
}

#[function_component(Card)]
pub fn card(CardProp { user }: &CardProp) -> Html {
    html! {
    <div class="m-3 p-4 border rounded d-flex align-items-center">
        <img src="https://robohash.org/hicveldicta.png?size=50x50&set=set1" class="mr-2" alt="img" />
        <div class="">
            <p class="fw-bold mb-1">{format!("{} {}", user.first_name.clone(), user.last_name.clone())}</p>
            <p class="fw-normal mb-1">{user.gender.clone()}</p>
            <p class="fw-normal mb-1">{user.email.clone()}</p>
            <p class="fw-normal mb-1">{user.phone.clone()}</p>
        </div>
    </div>
    }
}
```

The snippet above does the following:

- Imports yew dependency and the `User` model we created earlier
- Creates a `CardProps` component props with a `user` property
- Destructures the props to display the  user information in the UI

Finally, we must register the newly created components as part of the `components` module. To do this, open the `mod.rs` in the `components` folder and add the snippet below:

```rust
pub mod header;
pub mod loader;
pub mod card;
pub mod message;
```
**Putting it all together**
With the application components created, we can start using them to build our application by modifying the `main.rs` file as shown below:

```rust
use yew::prelude::*;

mod components;
mod models;

use gloo_net::{http::Request, Error}; //add
use models::user::Users; //add

use components::{card::Card, header::Header, loader::Loader, message::Message}; //add

#[function_component(App)]
fn app() -> Html {
    let users: UseStateHandle<Option<Users>> = use_state(|| None);
    let error: UseStateHandle<Option<Error>> = use_state(|| None);

    {
        //create copies of states
        let users = users.clone();
        let error = error.clone();

        use_effect_with_deps(
            move |_| {
                wasm_bindgen_futures::spawn_local(async move {
                    let fetched_users = Request::get("https://dummyjson.com/users").send().await;
                    match fetched_users {
                        Ok(response) => {
                            let json = response.json::<Users>().await;
                            match json {
                                Ok(json_resp) => {
                                    users.set(Some(json_resp));
                                }
                                Err(e) => error.set(Some(e)),
                            }
                        }
                        Err(e) => error.set(Some(e)),
                    }
                });
                || ()
            },
            (),
        );
    }

    let user_list_logic = match users.as_ref() {
        Some(users) => users
            .users
            .iter()
            .map(|user| {
                html! {
                  <Card user={user.clone() }/>
                }
            })
            .collect(),
        None => match error.as_ref() {
            Some(_) => {
                html! {
                    <Message text={"Error getting list of users"} css_class={"text-danger"}/>
                }
            }
            None => {
                html! {
                  <Loader />
                }
            }
        },
    };

    html! {
      <>
        <Header />
        {user_list_logic}
      </>
    }
}

fn main() {
    yew::start_app::<App>();
}
```

The snippet above does the following:

- Imports the required dependencies
- **Line 13 - 14**: Creates a `users` and `error` application state by using the `use_state` hook (similar to `data` function in Vue) and specifying `None` as the initial value. The `UseStateHandle` struct is used to specify the state type and the `Option` enum represents an optional value 
- **Line 18 - 19**: Creates a copy of the states for safe use within the current scope
- **Line 21 - 42**: Uses the `use_effect_with_deps` hook similar to the `created` hook in Vue to perform a side effect of fetching data from the [DummyJSON](https://dummyjson.com/) API asynchronously with the `wasm_bindgen_futures` and `gloo_net`'s  `Request::get` function. We also use the `match` control flow to match JSON response returned by updating the states accordingly
- **Line 44 - 66**: Creates a `user_list_logic` variable to abstract our application logic by using the `match` control flow to match patterns by doing the following:
    - Maps through the list of `users`  and pass the individual `user` to the `Card` component when the API returns appropriate data
    - Uses the `Message` and `Loader` component to match error and loading state, respectively
- **Line 68 - 73**: Updates the markup with the `Header` component and `user_list_logic` abstraction

With that done, we can restart a development server using the command below:
```bash
trunk serve --open
```

![](https://media.giphy.com/media/i9ADzKhTZh5zV0I8LB/giphy.gif)


[https://media.giphy.com/media/i9ADzKhTZh5zV0I8LB/giphy.gif](https://media.giphy.com/media/i9ADzKhTZh5zV0I8LB/giphy.gif)

# Conclusion

This post discussed how to create a web application using open API data from [DummyJSON](https://dummyjson.com/) and Yew.

These resources might be helpful:

- [Yew documentation](https://yew.rs/docs/getting-started/introduction)
- [Official Rust book](https://doc.rust-lang.org/book/)
- [WebAssembly Technology](https://webassembly.org/)