porting-a-javascript-app-to-webassembly-with-rust

20. 12. 2019


Rust

TL;DR

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

This is the first part of a blog post series.

Motivation

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.

[package]
name = "kartevonmorgen"

[dependencies]
seed = "0.5"
wasm-bindgen = "0.2"

[lib]
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.

#[macro_use]
extern crate seed;
use seed::prelude::*;

#[derive(Default)]
struct Mdl {
    // TODO
}

#[derive(Clone)]
enum Msg {
    // TODO
}

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

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

#[wasm_bindgen(start)]
pub fn render() {
    seed::App::builder(update, view).build_and_start();
}

Build the project with

wasm-pack build --target web

and serve it with

microserver

This is what you should see:


Hello Rust

Congratulations!

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:

#!/bin/bash
for f in `find src/ -type f ( -iname *.js -o -iname *.jsx )`
do
  git mv `echo $f` `echo $f | sed -e "s/.jsx?/.rs/g"`
done
./rename-js-and-jsx-to-rs.sh

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:

[dependencies]
seed = "0.5"
wasm-bindgen = "*"

### JS DEPENDENCIES ###

# 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.

#!/bin/bash
for f in `find src/ -type f -iname *.rs`
  do
    awk -i inplace '{print "// TODO: " $0}' $f
    git add $f
done
./comment-out-rust-files.sh

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
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.

the-state-of-javascript-2019

We were pretty sure 2018 would be the last time we did this survey. After all, the JavaScript ecosystem can’t very well keep changing again, can it?

But what do you know, turns out JavaScript isn’t quite done changing just yet! And so after over 21,717 respondents took this year’s survey we had to dig up our components and charts, curse us-from-a-year-ago for writing such crappy code, and get to work digging through the data.

We had some help though. The amazing Amelia Wattenberger joined the team as a guest dataviz expert to contribute a fresh new look on our data, and you’ll be able to explore her work in the Overview section.

We also borrowed a couple new visualisations from the State of JS’s sister survey, the State of CSS. We recommend checking it out if you haven’t done so yet!

Team

The State of JavaScript Survey is created and maintained by:

Be sure to check out my React/GraphQL JavaScript framework, Vulcan.js, as well as Raphaël’s React data visualization library nivo.

Download Our Data

You can download the raw JSON data for this survey. Let us know if you end up making your own data visualizations!

Completion Percentages

Because all survey questions could be skipped, some questions ended up with fewer answers than others. So we’ve added a small pie chart indicator to each question to let you know which proportion of total respondents answered it.

Credits & Stuff

The site is set in IBM Plex Mono. Questions? Feedback? Get in touch!

And now, let’s see what JavaScript has been up to this year!

– Sacha and Raphaël

javascript-fundamentals:-variables

Posted on December 27, 2018

Storing values that you can later reference is one of programmings most basic and fundamental concepts.

In JavaScript these named containers are called variables. Variables can be declared by using three different reserved keywords: var, letor const.

Each way differs in how you can interact with the variable later on but all are refereed to as “untyped”. This means they do not have any type attached, allowing for any type to be assigned.

The example below shows you how to declare a variable and then define the variable by giving it a value.

Note: A variable must always be declared before you can use it.

var exampleVariable; //declaration
exampleVariable = 'test value'; //definition
var exampleVariable = 'test value'; //shortened variation

Multiple variables can be declared in one statement:

//Creates three variables in one statement
var variableOne = 'First',
 variableTwo = 'Second',
 variableThree = 'Third';

Variables can also be re-declared many times over by overriding them:

var example = 'test value';
// example holds the value - 'test value'
example = 'new value';
//example now holds the value - 'new value'

let (es2015)

let variableName = 'variable value';

Introduced in ES2015, let encourages the philosophy that variables should only exist where they are needed.

Just like var, let variables can be reassigned at any point in the program but three main differences exist:

  1. At the top level, let , unlike var , does not create a property on the global object.
  2. let variables are not initialised until their definition is evaluated. Therefore, unlike var, let variables aren’t “hoisted” to the top of the block. Trying to access them will result in a RefrenceError.
  3. Variables declared using the var keyword are scoped to the immediate function body, while let variables are scoped to the immediate enclosing block denoted by { }.

const (ES6)

const variableName = 'variable value';

Introduced in ES6, const also allows you to define a new variable.

Just like let, const has block scope but one fundamental difference exists.

Once a const variable is initialised, its value can not be changed unless it’s part of an object that provides methods that mutate its content. For example, an array:

const arr = ['Bob','John','William'];
// console logging arr[1] returns 'Bob'
// The method below reassigns 'Bob' to 'Mike'
arr[1] = 'Mike';
// Console logging arr[1] returns 'Mike'
writing-javascript-with-only-six-characters

You’ve probably seen someone on the internet write funny-looking but runnable JavaScript code using only six different characters. But how does that actually work?

You can write basically any JavaScript program on the planet using just these characters:

This is a well-known trick, but not that many developers know how it actually works. Today, we’re gonna look at how it works under the hood. Let’s create the string "self" using only our funny little subset of characters.

Our target string will be "self", as an homage to the programming language Self for being one of the inspirations for JavaScript.

How It Works: The Basics

What allows us to make all other characters superfluous is the fact that we can abuse JavaScript’s type system and bizarre type conversion mechanisms.

We’ll be using our six superhero characters this way: with [] we can create arrays, with the negation and addition operators ! and we can do operations on them and finally use () to group operations.

Let’s start off with a simple array:

Negating an array with ! will coerce the array into a boolean. Arrays are considered to be truthy. Therefore, negating it will produce false:

Values of different types cannot be added together unless converted to similar types. JavaScript follows a pre-defined ruleset when doing conversions:

In the expression 2 true, JavaScript will convert true to a number, resulting in the expression 2 1.

In the expression 2 "2", JavaScript will convert the number of a string, resulting in 2 "2" === "22".

These conversion rules aren’t that bad, but it gets quite interesting quite quickly with other types.

JavaScript Array Coercions

Adding arrays together will convert them both to strings and concatenate them. Empty arrays turns into empty strings, so adding two arrays together yields the empty string.

[]   [] === ""   "" === ""

The same will happen when adding an array with something else, lets say, a boolean.

![]   [] === "false"   "" === "false"

Aha! Now we’re conjured a string containing the characters we need to produce our end goal, the string "self".

If we could produce some numbers, we could extract the characters we need in the right sequence:

"false"[3] === "s"

(![]   [])[3] === "s"

Let’s go looking for numbers!

In the previous section, we coerced an array to a boolean.
What happens if we coerce it to a number using ?

JavaScript will attempt to call valueOf on the array, which will fail and fall back to calling toString() on the array.

Converting a string to a number will produce the following results:

 "42" === 42
 "esg" == NaN
 "" === 0

The empty string is a falsy value, like null, undefined and the number zero, so converting any of these to a number produces zero:

 null === 0
 undefined === 0
 false === 0
 NaN === 0
 "" === 0

So converting an array to a number takes a detour converting to string first, finally producing zero:

Aha! We’ve managed to produce a number! Although, not a very useful one.
However, we can keep playing the coercion game:

!0 === !false
!false === true

!0 === true

Negating zero will make the falsy value of zero into a truthy boolean.
A truthy boolean as a number becomes… one!

Hooray! One one can become two ones… which is two. You get the point.

Using the substitutions we’ve learned to create numbers:

1 ===  true ==  (!0) ====  (!( [])) ===  ! []

1 ===  ! []
2 ===  ! []  ! []
3 ===  ! []  ! []  ! []
4 ===  ! []  ! []  ! []  ! []

Putting It All Together

Let’s put together all the things we’ve learned.

  • Arrays are truthful values, so negating them will produce false: ![] // false
  • JavaScript coercing rules state that adding arrays together will toString them: [] [] // ""
  • Converting an array to a number becomes zero, when negated becomes true, when coerced to number becomes 1: (!( [])) === 1
![]   [] === "false"
 ! [] === 1

(![]   [])[3]   (![]   [])[4]   (![]   [])[2]   (![]   [])[0]
^^^^^^^^^^      ^^^^^^^^^^      ^^^^^^^^^^      ^^^^^^^^^^      
  "false"         "false"         "false"         "false"       

^^^^^^^^^^^^^   ^^^^^^^^^^^^^   ^^^^^^^^^^^^^   ^^^^^^^^^^^^^    
      s               e               l               f         

Our final expression thus becomes:

(![]   [])[ ! [] ! [] ! []]   
(![]   [])[ ! [] ! [] ! [] ! []]   
(![]   [])[ ! [] ! []]  
(![]   [])[ []]

Adapting our expression to this specific JavaScript dialect, where newlines and whitespaces are, of course, banned:

(![] [])[ ! [] ! [] ! []] (![] [])[ ! [] ! [] ! [] ! []] (![] [])[ ! [] ! []] (![] [])[ []]

Easy as pie!

Thank you, Brendan Eich ❤️

September 10, 2019

My job nowadays involves a lot of music and JavaScript. You know what musicians really
care about? Paychecks (support your local musicians, go to concerts, don’t steal music
from indie musicians). But also: keeping time.

Keeping time in JavaScript is kind of a joke, not just because time is a
social construct (this is the Jenn Schiffer social engineering at work), but because it’s really easy to write
code that blocks the timekeeper. Remember: JavaScript
inherently only has one thread, which it uses for everything: painting your buttons,
looping through arrays, mining bitcoin, scrolling. Everything.
This means that most of the time, you
write blocking code, but it only blocks for a little bit – 1ms here and there. And that’s ok!
Visually you don’t notice that kind of latency, and let’s be honest: it takes like 400ms to download the scripts, what’s 1ms?

1ms starts getting in the way when it’s actually 5ms, or 40 ms, or when you’re
trying to have a metronome run correctly. I made a typing delay experiment to see how much delay people could tolerate, and just
for typing alone some people got really antsy around 200ms (shout out
to the section of the population who thought they were heroes because they could
tolerate infinity delay because of how bad ssh latency is. That’s not heroic,
that’s Stockholm syndrome. Complain to your sys admins).

When I changed that to an audio delay experiment,
musicians started complaining around 40ms. And that was just audio delay, not
an actual metronome. Imagine that fucking with your audio too! So, keeping time is really important – but how do we actually do that in JavaScript?

In general, when we want to not block in JavaScript (and do better than
setInterval, who is the friend you invite to a party but
shows up like /- 4h to it), we do one of two things:
start writing async functions, or move code to a Worker (Surma
has a great article about workers everyone
should read). In
particular, for audio things, there’s a third option: using the Web Audio clock – Chris Wilson has a great blog post
about how to do your own audio scheduling which is an oldie but a goodie! (turns out
not much changes in 4 years in the Web Audio spec world). Anyway, I wanted to
compare these three approaches, and see how bad the latency was.

Play with the experiment

Me being me, I made a whole demo to
test and compare these approaches. I built
3 kinds of metronomes:

  • a really bad one using setInterval on the main thread,
  • a less bad one using setInterval in a Worker,
  • the best one, that uses the Web Audio API to preschedule audio events, at
    the times you want (labelled “prescheduled” in the graphs). The audio events
    will happen precisely at the time they are scheduled, but if you want a
    callback to do some visual work on, that callback needs to be in a setTimeout,
    and will happen when it happens. This is why there are two lines for this metronome.

You can run them on your own in that Glitch, but if you only want
the results, here they are.

Results

Setup

There are 3 metronomes, that each tick 20 times, and after each tick, a callback
function is called. For the first 2 metronomes, in this callback you also
make the audio tick (except for the Web Audio scheduler metronome, which makes the audio
tick on its own time). The graphs below log the difference between the audioContext.currentTime
of successive ticks.

? The unrealistic case

This is when you’re literally doing 0 work in between the clock ticks. This is
probably never going to happen in a real app unless it’s … just
an actual metronome i guess. In this case, the difference between successive ticks looks
ok for all metronomes – I mean, why wouldn’t it be? You’re not scrolling, you’re
not doing any work, what’s there to block the ticks? There’s still a bit of variance between
each ticks, but that’s because we know we can’t schedule anything (except for the Web Audio
clock) to be exactly 0.5s away.

? The awful case

Here we are doing 0.5 seconds of fake work on the main thread, after each tick. This
is where things get really dodgy. Because that fake work is blocking, that means that all
the metronome callbacks are kind of screwed, and their ticks are delayed by at least 0.5s.
In the second metronome, even though we’re calling setInterval() in a Worker, it makes no difference because the work from the previous tick is blocking, so it automatically delays the next tick.
In the Web Audio case, we can hear the ticks correctly (the green line), but the callback (which you would use to display things to the screen), is delayed for the same reason
as the other metronomes. Friends don’t let friends do work on the main thread.

? The better, but still not great case

When we have a big chunk of blocking work, a good approach is to chunk it up in
smaller work. There are several ways to do this. I split each 0.5s of work into smaller
5ms chunks, and then do each of them in a requestAnimationFrame. This is ok,
but a bit wasteful (it makes your work take longer than necessary). A better
approach is to use tasks (see this sample code from the proxx game),
but the results weren’t going to be that different in this case, so I didn’t bother.
Anyway, this experiment looks better!
Now our ticks are only delayed by about 5ms, which might be ok for your use case. The bad main
thread setInterval metronome is still doing poorly because there’s still
work on the main thread and it keeps time on the main thread, so time is still
wibbly wobbly in this case.

? The optimal case

All workers all the time! If you can, do all this expensive work in a Worker!
If we move the work we have to do in the callback completely off the main thread,
then this setup basically looks the same as the unrealistic “there’s no work being done ever”
case – the key distinction is that it’s really “there’s no work being done on the main thread ever. Hurray!

What have I learned from this

  • time is hard
  • I knew setInterval() is bad for time keeping, but now I know it’s like … really bad
  • if you need audio scheduling, use the Web Audio clock
  • if you need accurate scheduling without the Web Audio clock, use setInterval
    in a Worker
  • and if you can, move any expensive work that you have to do from the main thread
    to a Worker.

Hope this helps at least one of you!


Thanks to Surma for proof reading this and letting
me steal his horrific “block for a fixed time” sample code (it’s this. I know you want to look).

javascript-style-guides

A collection of hand-picked JavaScript style guides.

  1. HTML/CSS Style Guides
  2. React Style Guides
Demo image: Google JavaScript Style Guide

About a style guide

Google JavaScript Style Guide

This document serves as the complete definition of Google’s coding standards for source code in the JavaScript programming language. A JavaScript source file is described as being in Google Style if and only if it adheres to the rules herein.

Demo image: Airbnb JavaScript Style Guide

About a style guide

Airbnb JavaScript Style Guide() {

A mostly reasonable approach to JavaScript.

Demo image: Airbnb CSS-in-JavaScript Style Guide

About a style guide

Airbnb CSS-in-JavaScript Style Guide

A mostly reasonable approach to CSS-in-JavaScript.

Demo image: JavaScript Language Coding Style

Date

Author

Ilya Kantor

About a style guide

JavaScript Language Coding Style

Our code must be as clean and easy to read as possible. That is actually the art of programming – to take a complex task and code it in a way that is both correct and human-readable. A good code style greatly assists in that.

Demo image: WordPress JavaScript Coding Standards

Date

Author

WordPress

About a style guide

WordPress JavaScript Coding Standards

JavaScript has become a critical component in developing WordPress-based applications (themes and plugins) as well as WordPress core. Standards are needed for formatting and styling JavaScript code to maintain the same code consistency as the WordPress standards provide for core PHP, HTML, and CSS code.

Demo image: JavaScript Standard Style

Date

Author

Standard JS

About a style guide

JavaScript Standard Style

JavaScript Style Guide, with linter & automatic code fixer.

Demo image: function qualityGuide

Date

Author

Nicolás Bevacqua

About a style guide

function qualityGuide () {

This style guide aims to provide the ground rules for an application’s JavaScript code, such that it’s highly readable and consistent across different developers on a team. The focus is put on quality and coherence across the different pieces of your application.

Demo image: Principles of Writing Consistent, Idiomatic JavaScript

Date

Author

Rick Waldron

About a style guide

Principles of Writing Consistent, Idiomatic JavaScript

This is a living document and new ideas for improving the code around us are always welcome.

Demo image: JavaScript Style Guide

Date

Author

Khan Academy

About a style guide

JavaScript Style Guide

This guide is adapted from the jQuery style guide.

Demo image: Node.js/JavaScript Style Guide

Date

Author

Felix Geisendörfer

About a style guide

Node.js/JavaScript Style Guide

A guide for styling your node.js / JavaScript code.

how-does-javascript-affect-your-seo?

Introduction

When choosing the tech stack for your application, you have to make several considerations. The programming language and framework that you choose affect the development time, the application’s performance, and its discoverability online.



One of the most important ways of getting discovered online is via organic searches through search engines. Search engines decide which results will be displayed based on a few key factors. These are generally in the control of the developer, and you can “optimize” them to improve the search-ability of your application. This is known as Search Engine Optimization (SEO), and it is one of the most important aspects in building and marketing your product.



In the next section, we will discuss some of the most essential SEO tags that every webpage should have. Then we’ll move forward into framework-based SEO considerations.

undefined

Title

The title is one of the most important parts of a page’s SEO. This is the title that is used by search engines when displaying your page in the results list. It is also the title used when you share your page on social media. You can set your webpage’s title like this:

 
 	Page Title
 

Description

The description of the page is the description that appears below the title in search engine results. It is also the description used in shares. To set the description for your webpage, just add this:

 
 	
 

Open Graph Image 

This tag doesn’t matter much in search engine results, but it’s vital for social media. It allows you to choose which image to display when the page is shared on social media websites like Twitter, Facebook, and LinkedIn. Set an attractive image via this tag to ensure that your link attracts a lot of attention.

 
 	
 

 A setup with all the necessary SEO tags would look like this:

 
 	Page Title
 	
 	
 

In the next section, we will discuss how you can insert these tags into your webpages according to the tech stack of your application.

Types of frameworks and their implications

No framework—pure HTML

If you’re not using any Javascript-based framework to build your application, all the SEO tags are in your control for each page via the HTML file. Therefore, no special library is required to set the tags.

Search engine crawlers also like this setup because it allows them to easily crawl your pages.

Using a framework (JS or server-rendered applications)

As we talk about optimizing SEO for framework-rendered apps, we’ll focus specifically on the React framework and on optimizing for the Google search engine. However, the following section will still be relevant to all frameworks and search engines.

In React, the most common way of rendering is client-side rendering. In essence, it’s a single HTML root file with a React script attached to it that renders the webpage at runtime. All routes are generated over that specific HTML file only. This happens on the client’s side after a route is queried and the data is received. This is why it’s called client-side rendering. However, the developer doesn’t have control over the SEO tags in this case since only a single HTML file exists. So, in cases like these, certain libraries are used to set SEO tags dynamically.

But there is another issue that client-side–rendered apps face. Search engine crawlers can’t crawl these webpages properly because the pages are generated at runtime. To solve that, certain additions have been made by the developers of web crawlers. For example, the Google web crawler queues JS-rendered pages for rendering if a page is detected to be JS-rendered.

However, the search engine indexing for such a page is delayed until the JS is rendered and the page is readable by a bot. The bot has to do this for every single page in your website. This is a long process, so errors occurring at any step will prevent that page from being indexed by the search engine.

undefined

To combat these problems, Google suggests the following solutions:



Pre-rendering 

Pre-rendering is the technique of converting client-side–rendered applications into static HTML files through the process of rendering. A pre-rendering tool renders the application by visiting each route individually and generating an HTML file for each one of them. However, this process becomes quite slow for larger applications, and dynamic linking is not possible in a pre-rendered React app since each page has to be present at compile time. So, it is limited to static pages or fetching dynamic content using query parameters.

Isomorphic rendering (client server side)

Isomorphic rendering is also known as hybrid rendering. When a user-agent, such as a Google bot, queries a URL of such an application, a server-rendered page is sent. Otherwise, a client-rendered page is sent to the rest of the users. This ensures that search engines index the page correctly and that client-side rendering still works for other clients. However, this type of rendering is very complicated to use and doesn’t offer any advantages over completely server-rendered React. Therefore, it isn’t widely used and doesn’t have any good packages or libraries for most frameworks.

Server-side rendering

In server-side rendering, the page is completely rendered on the server side before being sent to client. So, the client gets a complete HTML page as a response. This is good for SEO too, as search engine crawlers get a completely rendered webpage, which makes their job easier. In turn, it also increases the speed of your pages being indexed by the crawlers.



In the next section, we’ll discuss the best libraries in each framework that can be used to implement best SEO practices in your frontend.

Optimizations for specific frameworks

1. React-based frameworks

undefined

Client-side rendering

When React is rendered on the client’s side, react-helmet can be used, which allows a user to generate meta tags while rendering each page.



Pre-rendering

You should use react-snap or react-snapshot when using create-react-app. GatsbyJS is also a good framework for rendering React applications to static HTML files.





Server-side rendering

You should use a framework like NextJS to perform server-side rendering with React. This will allow the search engines to easily index your webpages.

2. NodeJS/ExpressJS 

undefined

Pre-rendering

 The library prerender-node works with any Node-rendered framework to render all routes as static webpages.



Server-side rendering

NodeJS is a server-side language, and Express is a routing framework for it. So, you get server-side rendering out of the box with Node. The only thing you’ll need to take care of is setting the SEO tags dynamically via ejs.

3. AngularJS 



undefined

Client-side rendering

You can use a library like ngx-seo-page when working with client-side Angular. It allows you to dynamically set the SEO tags during page render.



Pre-rendering

Modules like angular-prerender can be used to pre-render Angular apps. It visits both server and client routes and combines them to form a static client.



Server-side rendering

Angular Universal provides native support to Angular for server-side rendering the apps. You can combine it with ngx-seo-page to set SEO tags on your server-rendered application.



4. VueJS 

undefined

Client-side rendering

For client-side rendering with Vue, there are not many frameworks that allow dynamically setting SEO tags. One of them is vue-seo. However, its last update was two years ago, so pre-rendering or server-side rendering is preferred for better SEO.



Pre-rendering

To pre-render a Vue single-page application, an app like vue-cli-plugin-prerender-spa can be used. It is a robust solution that works with zero configuration.



Server-side rendering

Using frameworks like Nuxt.JS, you can easily create server-rendered Vue applications. It allows you to render your application on the server, run a client-side app, or generate pre-rendered static files easily.

5. Django/Python 

undefined

Server-side Rendering

The default way of using Django is via server-side rendering. HTML templates are rendered server-side according to the data passed to them via the server. So, you get the benefits of setting dynamic SEO tags by default.

Did you know ButterCMS works seamlessly with all of these frameworks? Our newly launched WRITE API makes integration smooth for developers and our content dashboard makes churning out content easy for marketers.

undefined

Summarizing the Content (TLDR)

In summary, we see that client-side–rendered applications face issues in being indexed correctly by search engines, and developers also face issues while setting SEO tags in these applications. However, these challenges can be overcome by relying on a variety of solutions based on the frameworks and rendering techniques used.



What you can do further to improve your website’s SEO

To improve your application’s SEO, follow all the SEO guidelines presented in this article to establish basic SEO correctness. When using header tags within your website (

,

, etc.), ensure you use all the relevant keywords—keywords that should also be repeated throughout the accompanying content. If you do these things, you’ll be ranking high in the search results in no time!



Useful Links

Make sure you don’t miss out on any Javascript tips.