20. 12. 2019



We will demonstrate how to do a complete port of a web application from
React Redux written in
JavaScript to WebAssembly (WASM) with

This is the first part of a blog post series.


Maintaining Software that is written in a dynamically typed language like JavaScript
is costly.
Maintaining a JavaScript frontend that is build with React,
NPM, WebPack
and Babel is even more expensive.
Frequently we had situations where we just wanted to upgrade a single dependency
or a WebPack plugin resulting in hours of fixing compatibility issues. Moreover
due to the characteristics of dynamically typed languages you’ll never
really know if the upgrade of a library causes errors during runtime.

In the past we didn’t really had an alternative to JavaScript/NPM.
Of course there is the wonderful purely functional elm language
that offers the possibility to write reliable web frontends.
But once you want to leave the functional elm world (e.g. to interact with the JavaScript API)
you’ll have to spent a lot of time for building “bridges”.

The rise of WebAssembly (WASM) is a
great oportunity to combine the power of the JavaScript world with
the compile-time guarantees and runtime performance of Rust.

Within the last two years the Rust community created
over 10 web framworks
that could be used to build web frontends with WASM.
Most of them are proof-of-concepts but a few are serious projects.

We want to do a reality check of how far we could get using
Rust as a frontend language.
To do so we have choosen Seed as one of the
more mature framworks.

Moreover we want to demonstrate a real-world usecase instead of
implementing just one more TODO list app.
This is why we’re going to port the complete frontend of
kartevonmorgen.org to Rust.
The Karte von morgen is an open source project for mapping
sustainable initiatives and organisations.

Let’s get started 🙂

Step 1: Prepare

We assume that you’re already familar with Rust and its ecosystem.
Nevertheless we try to make it as easy as possbile for newcomers and
JavaScript developers.
If you’re used to Rust you can probably skip sections like this one.

Install Rust

For most users rustup should work.
Windows users can download the rustup installer.

Within this guide we use Ubuntu Linux.

First install some basic tools:

sudo apt-get install git curl build-essential pkg-config libssl-dev

Then run the following in your terminal:

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

Rust Installation

Now check if Rust is installed sucessfully:

rustc -V
rustc 1.40.0 (73528e339 2019-12-16)

Install wasm-pack

To be able to pack our web project for the web we need wasm-pack:

cargo install wasm-pack

Install cargo-watch

During the development you might want to trigger the
compiler whenever a file changes.
This is what cargo-watch is for.

cargo install cargo-watch

Install microserver

To locally serve your web application you can use your favorite webserver.
In this guide we’re using microserver

cargo install microserver

Step 2: Initialize a Seed project

Within our legacy JavaScript project we’re going to create a basic Rust project based
on the Seed framework.

git clone https://github.com/kartevonmorgen/kartevonmorgen
cd kartevonmorgen/
git checkout -b rust
cargo init --lib

Now we need to modify the Cargo.toml file.
Beside the name we have to add the depencencies
and tell Rust that this library is a cdylib crate.

name = "kartevonmorgen"

seed = "0.5"
wasm-bindgen = "0.2"

crate-type = ["cdylib"]

Then we move the existing index.html to the project’s root
and modify it.

mv src/index.html .

Because we won’t use WebPack and its plugins anymore we have to
replace all templating parts that match the pattern <%= ... %>:

- <%= htmlWebpackPlugin.options.title %>
  Porting JS to Rust

Additionally ensure that the document’s charset is set to utf-8:

<meta charset="utf-8" />

Now we have to append our script that initializes the WASM module.
Usually there is already a defined app container like

 <div id="app">div>

In this case add the following script tag:


Note: Older browsers don’t support ES modules (here we’re using Firefox 71).

To check if everything is working as expected we’ll start with a simple hello world app.

extern crate seed;
use seed::prelude::*;

struct Mdl {
    // TODO

enum Msg {
    // TODO

fn update(_: Msg, _: &mut Mdl, _: &mut impl Orders) {
    // TODO

fn view(_: &Mdl) -> impl View {
    div![h1!["Hello Rust"],]

pub fn render() {
    seed::App::builder(update, view).build_and_start();

Build the project with

wasm-pack build --target web

and serve it with


This is what you should see:

Hello Rust


Step 3: Move existing code and clean up

Because we don’t want to rewrite the frontend from scratch but port the existing code
we first have to rename all JavaScript (.js) and JSX (.jsx) files to Rust (.rs) files.
Instead of doing this manually you could write and run a little helper script:

for f in `find src/ -type f ( -iname *.js -o -iname *.jsx )`
  git mv `echo $f` `echo $f | sed -e "s/.jsx?/.rs/g"`

You can also remove obsolete files like .eslintrc, package-lock.json etc.
Within the package.json file we can find information that we can reuse
or at least keep as a reminder.

Most metadata can be directly moved to Cargo.toml like the following fields:

  • name
  • version
  • description
  • repository
  • author
  • license
  • homepage

Other information like dependencies or scripts can’t be reused.
But instead of throwing them away we move them as comments into
the Cargo.toml, marked as TODO. They will serve us as a reminder
later to decide which equivalent Rust libraries we might need as a
replacement for those JS dependencies.

Here is an example how it could look like:

seed = "0.5"
wasm-bindgen = "*"


# TODO: "@fortawesome/react-fontawesome": "^0.1.3",
# TODO: "i18next": "^10.6.0",
# TODO: "leaflet": "^1.4.0",
# TODO: "normalize.css": "^8.0.1",
# TODO: "purecss": "^1.0.0",
# TODO: "react": "^16.8.2",
# TODO: "react-dom": "^16.8.2",
# TODO: "react-i18next": "^7.13.0",
# TODO: "react-leaflet": "^2.2.0",
# TODO: "react-redux": "^6.0.0",
# TODO: "redux": "^4.0.1",
# TODO: "redux-form": "^8.1.0",
# TODO: "redux-thunk": "^2.3.0",

When we find a matching Rust libary we can replace the TODO
with the actual dependency or otherwise drop it entirely if
it is not required for the new Rust implementation, e.g.
babel, webpack, etc.

In our case all the scripts were obsolete:

  • lint is now cargo fmt
  • test is now cargo test
  • watch-test is now cargo watch -xt
  • pack is now wasm-pack build --release --target web

Don’t forget to update your README.md and CONTRIBUTING.md.

Step 4: Create modules

In the beginning we will keep the structure and names of the original JS app.
Resist the temptation to rename the files and modules according to Rust’s
naming conventions, i.e. replacing CamelCase with snake_case. Eventually
we will do this, but not now.

First comment out the entire contents of all source files so that we can safely
import them without getting build errors.

for f in `find src/ -type f -iname *.rs`
    awk -i inplace '{print "// TODO: " $0}' $f
    git add $f

Then we recusively define our modules.
Within lib.rs we declare the top-level modules:

mod Actions;
mod GeoLocation;
mod Store;
mod WebAPI;
mod components;
mod constants;
mod i18n;
mod index;
mod rating;
mod reducers;
mod route;
mod util;
mod widgets;

In each folder we have to create a mod.rs file that lists
all contained submodules.

As an example our src/components/mod.rs looks like this:

pub mod App;
pub mod EntryForm;
pub mod Flower;
pub mod LandingPage;
pub mod Map;
pub mod SearchBar;
pub mod SelectTags;
pub mod Sidebar;
pub mod pure;
pub mod stories;
pub mod styling;

And this is the resulting file structure:

$ tree src
├── Actions
   ├── client.rs
   ├── mod.rs
   └── server.rs
├── components
   ├── App.rs
   ├── EntryForm.rs
   ├── Flower
   │   ├── FlowerLeaf.rs
   │   ├── index.rs
   │   └── mod.rs
   ├── LandingPage.rs
   ├── Map.rs
   ├── mod.rs
   ├── pure
   │   ├── AddressLine.rs
   │   ├── BusinessCard.rs
   │   ├── CityList.rs
   │   ├── Contact.rs
   │   ├── EntryDetails.rs

Summary and next steps

We have created a basic Seed web application within an existing JavaScript project.
We moved existing JavaScript code to commented-out Rust files. We defined Rust
modules that initially mirror the legacy JavaScript application structure.

In the next series we’ll see how to translate the legacy JavaScript code to
working Rust code.

Leave a Reply

Your email address will not be published. Required fields are marked *