Single-page apps for starters: what JS frameworks do under the hood

Marcos Sandrini
8 min readJul 30, 2021

In 1996, when I wrote my first piece of web-related code ever to make a “personal page” in the pre-social-media days, things were very easy for whoever wanted to make a website. You learned HTML and, well, that was it.

It was easy, but it was also frustrating to deal with the HTML limitations at the time. Those limitations and the inherently marvellous and chaotic character of the Web made us extend web development beyond the imagination. Once an architecture that could be considered minimal, it evolved into something much more complex and, should I say, confusing. There are often so many layers on top of each other interacting in non-obvious ways that it is easy even for experienced developers to lose track of what is being actually delivered among so many “helper” tools and “automatic” processes.

As my journey as a developer is rather long, I can consider myself somewhat lucky to have seen those pieces of the stack being slowly added on top of each other from time to time, so I could have enough room to understand what they do and thus the need for them (or the lack thereof). However, people who want to start developing for the web right now have much less time to digest all those different technologies and libraries and the reason why they are there. They are thrown at a job market situation that demands knowledge of a lot of tools and everything may seem highly arbitrary and hard to get. That is why I wanted to write this little primer.

The surge of JS frameworks

Nowadays the Web speaks frameworks and I do not think this is necessarily good or bad. To me, it is just a consequence of how things came to be. As you probably know, it was not always this way.

The boom of frameworks can be explained by going way back to what I call the “JQuery era”, a period (roughly from 2007 to 2013) in which the Web was already in a fairly good state to have pages and web apps with full interaction but it was still very impractical to develop for the Web using the native stack, due to a gigantic set of differences between browsers on how they did things back then (mostly Firefox vs. Internet Explorer). JQuery was a framework that enjoyed massive success back then by making development for the Web more palatable across browsers, bridging the existing gaps and putting some cherries on top, like an easy-to-use AJAX API, general helpers and even a UI plug-in library with some ready-to-use UI elements.

It allowed people to deal with HTML in a more streamlined way, like traversing more easily the hierarchical tree of HTML elements in JavaScript, what is called DOM (Document Object Model). During this “JQuery era” the development for the Web started to lean heavily towards what is called Single-Page Applications (SPAs), and people realised that JQuery was not well suited for this new thing, which we are going to get into soon. In a few years it became a de-facto legacy lib, still used (although it shouldn’t be) and maintained but surpassed on popularity by SPA-optimised libs like Angular 1 and then mostly React and Vue (with Angular 2+ still used and Svelte gaining traction). Those tools make the process of developing a SPA a relatively easy one, especially when compared to JQuery and native JS.

What lies below the surface

Even though they became today’s standards for web development, it is fundamental to know that today’s industry-standard JS frameworks are, still, only abstractions on top of JavaScript and, in the end, their outputs are pretty similar. It doesn’t matter if your framework of choice comes with a whole new language (like Elm), if you are working in TypeScript with SASS or CSS-in-JS, in the end, the utilities around the frameworks will deliver to the final user’s browser only static HTML, static CSS and plain JS (we will see this in detail later). WebAssembly may change this soon, but up to now, all popular JS frameworks deliver the same kind of asset, which is what browsers can understand.

Because of that, it is ideal that any developer should be able to do anything that either of those frameworks does without them if necessary, independently of whether you should actually do it or not. Most great Frontend developers I know of are pretty capable of writing a SPA in plain HTML/JS/CSS, although most would prefer not to do it without the help of frameworks.

Also, the more you know about what exactly frameworks do and how, the more comfortable you will be about working with them, minding their upsides and downsides. You could be well prepared to take advantage of the upsides and not be caught by the downsides.

You could just as well even make a choice of not using any framework at all if desired. In fact, in some specific situations, it may be advantageous to do things without frameworks. The resulting code that will be run from the user side will be much slimmer and the performance can be much better. Even maintainability can improve, although this one will probably not be necessarily true for bigger projects (that is, in fact, one of the selling points for frameworks right now).

What do all those tools do?

Even if it is not the case to ditch frameworks, it is important to know that the SPAs we generate with frameworks like React and Vue are just web pages, subject to the same rules as other “less smart” pages would be. This means that they are nothing more than:

  • a little initial HTML, that can be made bigger with server-side rendering (SSR), a technique with its own helper tools used to show more content in this initial HTML to improve the “first-paint” experience (that is, the important bit of experience for the user in the first moments of interaction before most interactivity assets are loaded);
  • some CSS, sometimes extracted from JS framework code by bundlers and thrown on the initial HTML’s <head> section, to avoid the risk of pages appearing without their corresponding style sheets;
  • finally, a ton of JavaScript routines, that basically deliver rendered views to the user. This code, in simple terms, has its logic and templates to generate “temporary” HTML trees, highly interactive of course, that will be redrawn by the same JavaScript routines, should they ever need to change. We’ll get to that in a minute.

Bear in mind that this is not a solo work from Frameworks. They have plenty of help for that: bundlers, specific interpreters and compilers, package managers, not to mention things outside of the direct delivery flow like test tools and code styling tools like linters.

Fundamental concepts to understand SPAs

Some concepts are essential to understand the mindset behind modern JS frameworks and, although none of them is exclusive to those frameworks, they are frequently associated with them. I will dive into four of them: modules, components, state and reactivity.

Modules

Developers from most languages usually work on smaller specialised files that can be “imported” onto others, with this meaning they can be called from other files that can, then, use their functionality. Having a file that can be imported as a whole or in smaller pieces on them is what is called a module. This is a normal practice in any programming language since immemorial times, but relatively new to Frontend.

Modules are supported on modern browsers natively but usually, they are handled by bundlers (Webpack, Rollup, Parcel, Snowpack) that joins the files selectively to be delivered to the browser. Natively, only JS files can be imported but bundlers can work with basically any type of file that can be an asset to be used in a Frontend project, including not only plain JS and CSS files but also media files like images and files that are processed (often with third-party libraries) before being delivered to the browser, like SASS (that is converted into CSS), TypeScript (converted into JS) and special files like .vue files (converted into both CSS and JS).

One small but important thing about modules: we can deliver plain JS files to users without any processing or optimising and it will work. However, valuable bytes with information that is very important to developers but not so much to users like redundant spaces, comments and indentation can be omitted from the “production” code to be delivered, and this is what the process called “minification” does, among other optimisations to achieve smaller files.

Components

This concept is tied to modules, as usually components are separated modules. A component is simply a function or a class (which is pretty much the same as function in JS) that can have its own template, data and functionality, isolated from the rest of the application. Most of this is achieved by the natural isolation that functions have and, in practice, they are building blocks of highly interactive templates with their own inner logic, that can be stacked on a tree-like structure.

State

This can be referred to with other names too, but in simple terms, the state is the data that components have to deal with internally for their logic, and this data can be only locally used or global. This data is used in making the application reactive to changes, so they cannot be plain variables (more on that ahead). Most JS frameworks offer great support for local state variables but for managing and organising the global state there are supplemental tools like Redux, VueX and MobX to help with the task, as the global state can become messy in a big project.

Reactivity

Reading above you may wonder what could be so great about state, or why we don’t simply use plain JS variables instead. The answer is that SPA component templates bear references to these state variables and if these variables change the templates also have to change.
Redraw the templates whenever needed and do it efficiently is the tricky part, though. Frameworks use varied techniques not only to track changes on state variables from within the components but also to infer the extension of those changes on the HTML currently rendered.

To infer how much of a redraw to the HTML is needed, one of the popular techniques is what is called “Virtual DOM”, which puts on JavaScript memory a copy of the HTML tree so it can have a quick comparison to check what has changed without having to traverse the actual HTML (DOM) tree, which is totally possible but quite slow. The Virtual DOM is used by React and Vue, the two most popular frameworks as of 2021 (although React may not technically be a framework, it is put on that shelf for ease of comparison). There are techniques claimed to be as even better than this used by frameworks like Svelte, but this is not an intended discussion for now.

Conclusion

With these concepts above you can get the main idea. So, these much important JS frameworks are not mystical constructs that evoke magical powers from the underworld, they are just libraries built on JS that are basically in charge of rendering interactive and portable templates to a web page, making the development of a full-fledged web app feel easier and less constrained. Independently of how it feels to develop and aside from how smart those tools may be, though, it is important to know that, after the “production build” is generated, the users will still be accessing what is technically a web page, made of HTML, CSS and plenty of JavaScript.

--

--

Marcos Sandrini

Designer and front-end programmer with 20+ years of experience, also a keen observer of the world and its people