Choosing Between React, Elm and Svelte
Utilizing JS frameworks to enhance existing web applications9 September, 2019
Current State of Affairs
My current projects are all built using PHP frameworks and sprinkling some JS/jQuery on top.
It’s not that I have been living in a cave. I am fully aware of javascript frameworks, and specifically at work I mostly write React these days.
Converting to one of these just for the sake of it would not be practical. And to be honest I am not entirely convinced it is appropriate for my specific use case.
On the other hand, there come times when the jQuery spaghetti starts to emerge when you want to go juuuust a little bit further with javascript, and you would really like to use a single React component right there.
So, what I am currently searching for, and what this post is about, is to find the best way to introduce small JS widgets inside the application. These widgets should be easy to write, easy to maintain, and not take up more space than they need to.
The JS framework era
React (and js frameworks in general) offers a very smooth user experience by loading the whole app once and then working from the client. It manages to do that while at the same time saving the developer a lot of trouble, and making development honestly quite enjoyable.
That’s the enormous value the JS frameworks have brought to the market in my opinion: Scale.
Multiple people can work on a big React codebase which can be neatly organized with components. This would be unthinkable in the jQuery age.
The reason is that they have made it possible to write in a declarative way for the browser.
Utilizing Declarative Programming
Traditional imperative programming requires you (the developer) to instruct the browser command-by-command what you want it to do.
For example, this is imperative:
const button = document.getElementById('button');
button.setAttribute('style','color:#f00');
button.text = "Submit button"
This on the other hand, has the same result but is declarative:
<button style='color: #f00'>
Submit button
</button>
In a broad sense, declarative programming means you just describe the result, and let the underlying engine take care of it for you.
React. Elm. Svelte.
I have chosen three rather unlikely companions.
All three of them want to build Single Page web applications, and all three of them use different ways to accomplish that goal.
We are going to try and use each one to inject some functionality into an existing codebase.
Let’s build a counter
We will try to build something simple, which at the same time utilizes one of the most shought out features of these frameworks: state management
The resulting application will look like this:
React
React is built by Facebook to create large Single Page Applications while at the same time being as simple as possible.
Setup
We will be using a utility called nano-react-app that makes the setup process as painless and optimal as possible.
npx nano-react-app counter-react
cd counter-react
npm install
This will install all project dependencies needed
This is the code for the React application (using hooks to be more compact)
Code
src/App.js
import React, {useState} from "react";
export default function Counter(props) {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count+1)}>Increase Count</button>
</div>
);
}
We need to specify here the id of the node where our application will be mounted. Here I used the id “counter-react”
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("counter-react"));
And now we build our application for production.
Nano-react-app is using parcel (default build settings)
npm run build
Results
node_modules size: 111MB
counter-react.min.js size (minified, uncompressed): 126KB
We see that the resulting js file is quite big for such a small “application”.
This is because this includes the whole react library that’s needed in order to run our code.
In a fully-fledged application this of course would be not much overhead, but here in our case this feels quite bloated for the simple function we are trying to achieve. For comparison, the whole jQuery library that it’s so hip to hate on, comes up at 86KB
Elm
Elm is not as popular as react, in fact most of you might not even have heard it before.
Elm also compiles to JS, just like React does, and results in a standalone web application.
Unlike React though, Elm has its own syntax which is heavily influenced by functional programming.
What makes Elm a very attractive option to me as a developer, is that it offers incredible type safety that JS is not able to provide on its own. Functional programming is a paradigm that I really want to look into in the coming years and Elm is a perfect starting point.
Setup
npm install -g elm
mkdir counter-elm
cd counter-elm
elm init
Code
Create the main application file.
The syntax might look weird at first, but for someone who already writes React it’s not so hard to figure things out.
src/Main.elm
module Main exposing (Model, Msg(..), init, main, update, view)
import Browser
import Html exposing (Html, button, div, h1, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
Int
init : Model
init =
0
-- UPDATE
type Msg
= Increment
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h1 [] [ text ("Counter: " ++ String.fromInt model) ]
, button [ onClick Increment ] [ text "Increment Count" ]
]
By default, elm make
builds an HTML file with the whole SPA.
We just want the js though, which we can specify with the --output
flag
elm make src/Main.elm --optimize --output=counter-elm.js
uglifyjs counter-elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=counter-elm.min.js
Afterwards you can mount the app like this
<script src="counter-elm.min.js"></script>
<div id="elm"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('elm')
});
</script>
Results
node_modules size: 0B
counter-elm.min.js size (minified, uncompressed): 28KB
Now we’re getting there. Even though we include the whole Elm engine, the bundle size is much smaller and more representative of what we’re trying to achieve.
Svelte
I am not sure Svelte should be called a web framework because it’s on a whole category of its own, much closer to bare metal from what I can tell.
It challenges a lot of things we take for granted and I’m curious to see if it gains traction in the future.
Setup
The simplest way I found to get started, is to just play in the REPL and then download the code and build it for production locally.
Code
Svelte looks a lot like javascript but has a few unique identifiers. Not that hard to get used to.
<script>
let count = 0;
function increaseCount() {
count = count + 1
}
</script>
<h1>Count: {count}</h1>
<button on:click={increaseCount}>Increase Count</button>
After downloading from the REPL, run
Svelte is using rollup (default build settings)
npm install
npm run build
Results
node_modules: 25MB
counter-svelte.min.js (minified, uncompressed): 3KB
Now we’re talking.
Svelte is clearly the winner, being able to provide the most optimized declarative programming experience in JS.
Overview
This was a very nice experiment for me and gave me some perspective for what is out there. Here is how I would rank each of the frameworks and where I would use each one.
React
React has some very powerful pros on its list. Since it’s the most popular framework out there, it makes it ideal for big projects.
- It is maintained by Facebook so it’s not going away anytime soon.
- You can find modules and libraries for anything
- You can easily find help in any issues you have by the community
- You can easily find programmers who know React
All of these are very compelling reasons if you are a company looking to develop your next big thing.
React was built to create large Single Page Applications by many developers, and that’s where it shines.
Elm
I love Elm. The whole concept of functional programming with strict types and type inference just draws me. I feel it’s a higher form of programming, where you have a whole science (math) to back you up and make sure you eliminate a whole class of bugs.
I’m not claiming it’s a silver bullet, obviously you can write bad code at any language. This is just a personal preference and I definitely think I’m moving towards Elm.
Downsides? I havent’ developed anything big in Elm yet, but from what I can tell it’s a bit cumbersome to deal with sideffect-y things like web sockets etc.
Svelte
Svelte is very cool. I like its bare-minimum approach and the way it give you exactly what you need. If I need a small widget to go into an existing project of mine, this is how I’m building it.
However, I wouldn’t pick it for a large Single Page Application.
I don’t doubt it can fare well, there are a lot of things built in the language to handle the task. It’s just as I mentioned above, I’m moving towards functional programming and if I wanted to build a fully-fledged app, then Elm would be my language of choice.
Conclusions
This article presents arguably a superficial view of each language and does not get into the nitty gritty. It’s meant to document the process I went through and save you a couple of hours of going through the same.
Bundle size is just one metric. The reason I’m choosing to focus on size, is that I want to deliver functionality to the user in the most performant way possible.
For me and for the near future, svelte will probably be my goto for small js widgets, and Elm for SPAs.
Is this the best for you?
In the case of a Single Page Application there are many other variables to consider:
- Maintainability - will those languages be relevant/maintained in the future?
- Syntax - do you have people who can write in those languages
- Paradigm - Elm operates under a different paradigm from the rest of them. Do you want to buy into that?
- Popularity - what modules are available?
These are not in the scope of this article, but could be of another one in the future