Build a Web App in Rust with WebAssembly and Yew

by Didin J. on Jul 06, 2025 Build a Web App in Rust with WebAssembly and Yew

Learn how to build a blazing-fast web app in Rust using WebAssembly and Yew. Step-by-step tutorial with setup, code, and deployment tips.

Rust has quickly become a favorite among developers for its performance, safety, and modern tooling. But did you know that Rust can also be used to build web applications? Thanks to WebAssembly (WASM) and the Yew framework, it's now possible to write frontend code in Rust that runs in the browser—offering near-native speed and robust type safety.

In this tutorial, you’ll learn how to build a simple yet powerful web app entirely in Rust using Yew, a modern framework inspired by React. Yew leverages WebAssembly to compile Rust to the web, making it ideal for applications where performance and correctness are critical.

We'll walk through everything step-by-step—from setting up the toolchain and writing components to bundling and deploying your app. By the end, you'll have a fully functional Rust-based web app running in the browser.

What You’ll Build

To keep things beginner-friendly and focused, we'll create a basic counter web app that demonstrates:

  • Setting up a Yew project with Trunk

  • Using components and state in Yew

  • Handling events (e.g., button clicks)

  • Compiling and deploying to the web

Let’s get started by setting up the environment.


Prerequisites

Before we begin building our Rust + WebAssembly web app with Yew, make sure you have the following tools and dependencies installed on your system:

1. Rust and Cargo

Install Rust using rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

After installation, verify:

rustc --version
cargo --version

2. WebAssembly Target for Rust

Add the WebAssembly compilation target:

rustup target add wasm32-unknown-unknown

This allows Rust to compile code that runs in the browser via WASM.

3. Trunk (Build Tool for Yew)

Yew recommends using Trunk to build, serve, and bundle your app easily. Install it via Cargo:

cargo install trunk

Make sure it’s installed:

trunk --version

4. Node.js and npm

Trunk uses tailwindcss and other JS-based tooling internally, so make sure Node.js is available:

node -v
npm -v

You can install it from nodejs.org if needed.

With the environment ready, let’s move on to creating our Yew project and writing the first lines of Rust-powered frontend code.


Create a Yew Project

With the environment set up, it’s time to create your first Yew app. We’ll start by creating a new Rust library project and configuring it to work with Yew and Trunk.

Step 1: Create the Project Directory

Run the following command to create a new directory and initialize a Rust library project:

cargo new yew-web-app --lib
cd yew-web-app

We use --lib because Yew apps typically expose a lib.rs rather than a main.rs.

Step 2: Add Dependencies

Edit the Cargo.toml file and add the following dependencies:

[package]
name = "yew-web-app"
version = "0.1.0"
edition = "2021"

[dependencies]
yew = { version = "0.21", features = ["csr"] }
wasm-bindgen = "0.2"

Note: crate-type = ["cdylib"] is necessary to compile to WebAssembly.

Step 3: Create src/main.rs

Replace the contents of src/main.rs with a minimal Yew app:

use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let count = use_state(|| 0);

    let onclick = {
        let count = count.clone();
        Callback::from(move |_| count.set(*count + 1))
    };

    html! {
        <div style="text-align: center; margin-top: 50px;">
            <h1>{ "Yew Counter App" }</h1>
            <p>{ format!("Count: {}", *count) }</p>
            <button onclick={onclick}>{ "Click me" }</button>
        </div>
    }
}

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

Step 4: Add index.html

Create a new index.html in the root directory (same level as Cargo.toml):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Yew Web App</title>
</head>
<body>
    <main></main>
</body>
</html>

Step 5: Create a Trunk.toml (Optional but Recommended)

In the project root, add a Trunk.toml file:

[build]
target = "index.html"

You’re now ready to run your Yew app using Trunk!


Run and Test the Yew App with Trunk

With your Yew app and project structure updated, it’s time to build and run it locally using Trunk, the recommended bundler and dev server for Yew.

Start the Development Server

From the project root, run:

trunk serve --open

This command will:

  • Compile your Rust code to WebAssembly

  • Serve index.html on a local dev server

  • Watch for code changes and rebuild automatically

  • Open the app in your default browser

You should see something like this in your terminal:

🔧 compiling...
🚀 serving at http://127.0.0.1:8080
🌍 opening browser

Build a Web App in Rust with WebAssembly and Yew - yew counter

Test the Counter App

Once it opens in the browser, you should see:

  • A title: Yew Counter App

  • A counter starting at 0

  • A button labeled Click me

Clicking the button will increment the count—this demonstrates Yew’s reactive state management and component rendering in action.

Trunk File Serving Notes

Trunk automatically looks for an index.html in the root folder and handles script injection for your compiled .wasm and .js output. No need to manage wasm-bindgen or JS glue code manually.

You can customize this behavior in Trunk.toml if needed:

[build]
target = "index.html"
dist = "dist"

With the app running successfully, let’s move on to expanding the app structure and learning how to build more complex UIs.


Build a Functional Yew Web App

Now that you’ve got a basic counter working, let’s dive deeper into how Yew handles components, state, and events. We'll walk through building a more refined version of the app to better understand how Yew applications are structured.

Project Structure

To keep things clean, organize the app into components:

src/
├── components/
│   └── counter.rs
└── main.rs

Create the folder:

mkdir src/components
touch src/components/counter.rs

counter.rs: A Yew Function Component

In src/components/counter.rs, add:

use yew::prelude::*;

#[function_component(Counter)]
pub fn counter() -> Html {
    let count = use_state(|| 0);

    let increment = {
        let count = count.clone();
        Callback::from(move |_| count.set(*count + 1))
    };

    let decrement = {
        let count = count.clone();
        Callback::from(move |_| count.set(*count - 1))
    };

    html! {
        <div style="text-align: center; margin-top: 40px;">
            <h2>{ "Rust + Yew Counter" }</h2>
            <p>{ format!("Current count: {}", *count) }</p>
            <button onclick={decrement}>{ "-1" }</button>
            <button onclick={increment} style="margin-left: 10px;">{ "+1" }</button>
        </div>
    }
}

Create a file src/components/mod.rs:

pub mod counter;

Update main.rs to Use the Component

In src/main.rs, import and use the Counter component:

mod components;

use components::counter::Counter;
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    html! {
        <div>
            <h1 style="text-align: center;">{ "Welcome to Yew WebAssembly App" }</h1>
            <Counter />
        </div>
    }
}

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

Re-run the App

If Trunk isn't already running, restart it:

trunk serve --open

Your app now has a fully modular structure with reusable components—a key strength of Yew.

Build a Web App in Rust with WebAssembly and Yew


Compile and Deploy the Yew App

Once your Rust + Yew web app is working locally, it’s time to prepare it for deployment to the web. We’ll show how to build the app for production and deploy it using GitHub Pages and Netlify—both free and popular hosting options.

Step 1: Build the App for Production

Run the following command to generate optimized WebAssembly output:

trunk build --release

This creates a dist/ folder containing the compiled .wasm, JavaScript, and HTML files ready to deploy.

You can test it locally by serving it with a static file server:

npx serve dist


Deployment Options

Option 1: Deploy to GitHub Pages

  1. Push your project to GitHub
    Initialize git and push to a repo:

    git init git remote add origin https://github.com/yourusername/yew-web-app.git git add . git commit -m "Initial commit" git push -u origin main
  2. Install GitHub Pages CLI (optional)

    npm install -g gh-pages
  3. Deploy the dist/ directory

    npx gh-pages -d dist
  4. Configure GitHub repo settings
    Go to your repository → Settings → Pages → Select branch: gh-pages → Save

Your app will be live at:
https://yourusername.github.io/yew-web-app/

💡 Tip: Update your <script> or Trunk config to set a public URL base if using subpaths.

Option 2: Deploy to Netlify

  1. Log in and create a new site from GitHub or manually drag-and-drop the dist/ folder into Netlify

  2. Set the build command and publish directory in Netlify settings:

    • Build Command: trunk build --release

    • Publish directory: dist

  3. Deploy!

(Optional) Trunk.toml for Public URL

If deploying to a subpath (like GitHub Pages), add this to Trunk.toml:

public_url = "/yew-web-app/"

Now your app is live on the web—blazing fast, compiled from Rust, and fully reactive thanks to Yew and WebAssembly.


Final Testing & Conclusion

Final Testing

Before considering your deployment complete, it’s a good idea to do a final round of testing:

  • ✅ Load the app in different browsers (Chrome, Firefox, Safari)

  • ✅ Test both desktop and mobile views

  • ✅ Ensure that button clicks and state updates work as expected

  • ✅ Check console logs for any warnings or errors

  • ✅ Verify that assets load correctly from the correct URL path (especially if hosted under a subdirectory like GitHub Pages)

Conclusion

In this tutorial, you built a complete web app using Rust, WebAssembly, and the Yew framework—all without writing a single line of JavaScript.

You’ve learned how to:

  • Set up a development environment for Rust + WASM

  • Create a reactive UI using Yew components and state hooks

  • Build and serve the app using Trunk

  • Organize code into reusable modules

  • Deploy your app to GitHub Pages or Netlify

Yew brings the safety and speed of Rust to the frontend, opening up a new world of possibilities for high-performance, maintainable web applications.

You can get the full source code on our GitHub.

That is just the basics. If you need more deep learning about the Rust language and frameworks, you can take the following cheap course:

Thanks!