Exploring Yew — The Rust-based Frontend Framework
From a React developer’s perspective
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 and Yew as a React developer.
GitHub repository can be found here.
Similarities between React and Yew
The following are the similarities between the two technologies:
Prerequisites
To fully grasp the concepts presented in this tutorial, the following requirements apply:
- Basic understanding of React
- Basic understanding of Rust
- Rust installation
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:
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:
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:
cargo install trunk
Getting Started
To get started, we need to navigate to the desired directory and run the command below in our terminal:
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 React ecosystem.
On running the command, cargo
will generate a project directory with basic files as shown below:
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 React application.
Next, we proceed to install the required dependencies by modifying the [dependencies]
section of the Cargo.toml
file as shown below:
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 React 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:
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:
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 React developers
markup. The macro works similarly to JSX
in React.
yew::start_app::<App>()
: Starts a Yew application by mounting the App
component to the body of the document. It works similarly to ReactDOM.render
function in React.
HTML Render
Similarly to the way React 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:
Next, we can test our application by starting the development server by running the command below in our terminal:
trunk serve --open
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’s user API.
Module system in Rust
In React, components form 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.
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:
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:
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 ausers
property; an array type ofUser
struct. Dynamic arrays in Rust are represented as a vector
Sample of API response from DummyJSON below:
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:
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:
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:
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:
The snippet above does the following:
- Imports the required dependency
- Creates a
MessageProp
struct withtext
andcss_class
properties to represent the component property. The#[derive(Properties, PartialEq)]
macros marks the struct as a component prop similar to a React 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:
The snippet above does the following:
- Imports
yew
dependency and theUser
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:
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:
The snippet above does the following:
- Imports the required dependencies
- Line 13–14: Creates a
users
anderror
application state by using theuse_state
hook (similar tousestate
hook in React) and specifyingNone
as the initial value. TheUseStateHandle
struct is used to specify the state type and theOption
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 theuseEffect
in React to perform a side effect of fetching data from the DummyJSON API asynchronously with thewasm_bindgen_futures
andgloo_net
’sRequest::get
function. We also use thematch
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 thematch
control flow to match patterns by doing the following:
- Maps through the list ofusers
and pass the individualuser
to theCard
component when the API returns appropriate data
- Uses theMessage
andLoader
component to match error and loading state, respectively - Line 68–73: Updates the markup with the
Header
component anduser_list_logic
abstraction
With that done, we can restart a development server using the command below:
trunk serve --open
Conclusion
This post discussed how to create a web application using Open API data from DummyJSON and Yew.
These resources might be helpful: