DEV Community

Pratham Jagga
Pratham Jagga

Posted on

Ep. 3 - Social Media App in Rust + WebAssembly πŸ¦€πŸŒ

Hola Amazing Devs πŸ‘‹,

Welcome to the third episode of our Frontend Development in Rust and WebAssembly blog series. Earlier, we have understood the basics of WebAssembly and set-up the development environment.

Now, let's start with our first application: Social Media Application in Rust + Yew. So buckle up your seat belts as we'll be doing the Rust WASM Magic πŸͺ„. Also you can follow along if you'd wish to :)

Image description

Well, in this episode we'll start with a login form and in the next episodes we'll proceed ahead.

Prerequisites

  • Cargo and Rust is required.
  • Compilation target for WebAssembly must be added, for Rust to compile into WebAssembly: rustup target add wasm32-unknown-unknown
  • Next up, we require trunk for serving Yew applications: cargo install --locked trunk

Setting up a new Yew project

Follow the steps in the previous blog in this series (until step 4) to set up a new Yew project. After that, we'll end up with a src/main.rs file with some relevant Yew code and an index.html file in the root directory.

use yew::prelude::*;

#[function_component]
fn App() -> Html {
    html! {
        <h1>Hello from rust in browser πŸ¦€πŸ‘‹</h1>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}
Enter fullscreen mode Exit fullscreen mode

The flow

  • We'll create a function component LoginForm. This will contain the state variables for username, password and login message, the change handling methods for our inputs and a method for handling form submission. And finally, we'll have to return the Html Template by this function component (html!). html! is a type provided by Yew which helps write html like template code in Rust, just like JSX. This is a blessing from Yew πŸ₯Ή.

  • Then we just call our LoginForm component from our App.

  • And finally, we'll render the App within main method.

Writing the component

  • Navigate to src/main.rs, and add the following code right above App function:
use web_sys::HtmlInputElement;
#[function_component(LoginForm)]
fn login_form() -> Html {
    let username = use_state(|| "".to_string());
    let password = use_state(|| "".to_string());
    let message = use_state(|| "".to_string());

    let on_username_input = {
        let username = username.clone();
        Callback::from(move |e: InputEvent| {
            let input = e.target_unchecked_into::<HtmlInputElement>();
            username.set(input.value());
        })
    };

    let on_password_input = {
        let password = password.clone();
        Callback::from(move |e: InputEvent| {
            let input = e.target_unchecked_into::<HtmlInputElement>();
            password.set(input.value());
        })
    };

    let on_submit = {
        let username = username.clone();
        let password = password.clone();
        let message = message.clone();
        Callback::from(move |_| {
            if *username == "dev.to" && *password == "dev.to" {
                message.set("Login successful!".to_string());
            } else {
                message.set("Invalid username or password.".to_string());
            }
        })
    };

    html! {
        <div>
            <h1>{ "Login" }</h1>
            <input
                type="text"
                placeholder="Username"
                value={(*username).clone()}
                oninput={on_username_input}
            />
            <input
                type="password"
                placeholder="Password"
                value={(*password).clone()}
                oninput={on_password_input}
            />
            <button onclick={on_submit}>{ "Login" }</button>
            <p>{ (*message).clone() }</p>
        </div>
    }
}

Enter fullscreen mode Exit fullscreen mode

This code defines a function component with the name LoginForm, with three states: username, password and message, with empty strings as their initial values.

Then, at the last we are returing an HTML Template with a login form. Note that we are giving the values for inputs as *(input_state).clone() as we are in the closure, similarly we are doing for the message.

Also, for both of the inputs and the for the button, we are passing handlers. (the oninput and onclick).

In the input handler callbacks, we are just cloning the states, listening for the InputEvent, typecasting it to HtmlInputElement and setting the state to its value after typecasting. Since HtmlInputElement is from the crate web_sys, we also need to import it in our main.rs file and mention it as a dependency in Cargo.toml. Cargo.toml should look like:

[package]
name = "wasm-dev"
version = "0.1.0"
edition = "2021"

[dependencies]
yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
web-sys = "0.3"
Enter fullscreen mode Exit fullscreen mode

Now in the onsubmit handler, we check if the username and password values are correct, then we change the error message accordingly.

And do not forget to call our LoginForm component in the App component and render App in the main function:

#[function_component(App)]
fn app() -> Html {
    html! {
        <LoginForm />
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

Enter fullscreen mode Exit fullscreen mode

Finally we do: trunk serve and navigate to `localhost:8000 and Tadaa πŸ˜€:

Image description

We have our login form, written in Rust 😎.

Let's try example as username and example as password, you'll see te following message:

Image description

And trying the correct credentials: dev.to and dev.to:

Image description

Hurray, we made it.

So, this is it for today, in later blogs, we'll complete the authentication part and then create a news feed.

Final code can be found here on Github.

See you next week. Adios, Amigos πŸ‘‹

Image description

Top comments (0)