My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
Vanilla JavaScript vs. Frameworks & Libraries — Finding a good balance

Vanilla JavaScript vs. Frameworks & Libraries — Finding a good balance

Marco Alka's photo
Marco Alka
·Nov 3, 2016

Did you ever wonder, "Which JS framework should I use?". There are so many, and all of them want to help you build awesome web applications. Wouldn't it be great to merge them all into a single piece of awesomeness? Well, meet me, who has a slightly different opinion!

ATTENTION! The following article contains HTML5 source code, which follows WhatWG conventions of optional tags and JavaScript (JS) which uses the ECMAScript 2015 specification.

Overview

Live On The JS Side Of Life

Life in the JavaScript world is crazy. All the big companies push out new technologies every year, all the time. Everything is so fast paced and web developers must live on the bleeding-edge in order to satisfy customers, while also making sure that users with older clients can still enjoy the full glory of their creation. Websites and web applications, though looking simple, are becoming more and more elaborate. One could say, Web Development has become an art form, making sure every little detail is in place (pixel-perfect), and no screen resolution will destroy the beauty.

But what empowers developers to build sites, like Hashnode? Most people learn at school that websites are built with HTML, CSS and JS. Afterwards they make their own sites which usually look ugly. Do big companies, and great web artists really use these same tools? The answer is: Yes, yes they do! Then again, no they don't. It is very difficult, since most professionals today use a lot more tooling, but basically, everything is based on the same technology.

So, what kind of tools can one find out in the big, wide, wild world of the web? Frameworks. Libraries. Transpilers. And a lot more tools, with a lot more names, but for the sake of simplicity, let's just take a look at the three I've mentioned.

Frameworks

If you know a programming language, or if you have ever browsed a tech-news site, you might have already stumbled over the word "framework". Maybe you already have a gist of it. It sounds like something you can use to build something else. That would be its simplest description. A more appropriate description would be: A framework is an opinionated piece of software which can be used as a foundation for a wide varieties of applications, which have a specific problem, for which the used framework was created (Source). Opinionated in this context means, that code written on the top of a framework has to follow certain arrangements and definitions, or it must be written in a certain style to work with the framework. Consequently, a framework dictates your application architecture (Source) and usually a framework is the piece of code which calls your implementation (Source).

In today's world, there are some really popular frameworks, like AngularJS, EmberJS and LoopBack. All of them have a different focus and you will have to decide for one. You simply can't use AngularJS together with EmberJS since they basically aid you in solving similar problems, but come with different implementations.

Practical example; Angular looks like this:

<!doctype html>
<html lang="en" ng-app>
  <meta charset="utf-8">
  <title>My HTML File</title>
  <script src="angular.js"></script>

  <p>Nothing here {{'yet' + '!'}}

...while Ember looks like the following:

<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>My HTML File</title>
  <script src="ember.js"></script>
  <script src="handlebars.js"></script>

  <p>Nothing here {{time}}

What you can see is that they look nearly the same. Both use {{ and }} which leads to a lot of confusion, and the frameworks will not work when mixed.

Libraries

Libraries, compared to frameworks, are pieces of software which usually just help you do a simple task with ease. Or again, the more elaborate definition: A library is an unopinionated piece of software which implements a certain functionality with a defined API a developer can call from their application. It's quite easy, actually. Let's compare it with a real world library. You go there and it is filled with books. You pick one and read it. It's the same for programming. You "go" to a programming library, and it is filled with functions. So you pick one and call it (Source). Reading a book does not change how you work internally. It's still mother nature's doing (our framework). But you can use the book nevertheless.

Examples for popular libraries include jQuery, underscore and lodash. Even though underscore and lodash, basically solve the same purpose by providing a utility belt of JS functions, you can use both in the same project without them interfering with each other.

// Node.JS
const lodash = require('lodash');
const underscore = require('underscore');

// Easily use either of underscore or lodash here :)

Transpilers

Transpilers, compared to frameworks and libraries serve a completely different purpose. You neither build your application on the top of them, nor include them (or any of their functionalities) in your projects. These are the tools meant to refine your creation. You should just care about writing easy to handle code. A transpiler has the task to transform it into something which you can actually ship. Usually you use it to transform a different language (like TypeScript) to JS or remove bleeding-edge features, like let, from your source, so older browsers can still execute it. After transpiling code, it is usually further improved by minification for performance reasons (saving bytes can make a big difference, especially for mobile users with limited data volume or access speed).

One of the most well-known transpilers is Babel, which can be extended by plugins to do quite a lot of transformations for you.

// ES6 code
const foo = ({ opt1 = '', } = {}) => {

  alert(opt1);
};

foo();

will be transpiled to (Babel v6.17.0)

// Transpiled to JS v1.8.5
'use strict';

var foo = function foo() {
  var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

  var _ref$opt = _ref.opt1;
  var opt1 = _ref$opt === undefined ? '' : _ref$opt;


  alert(opt1);
};

foo();

Just for the sake of completion, the source after minification will look like this:

"use strict";var foo=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=t.opt1,r=void 0===o?"":o;alert(r)};foo();

I Found A Framework You Will Love...

From the above definitions, couldn't one come to the conclusion that developers should first search for a framework which handles their specific case, then add libraries which implement needed functionalities, write some code to glue everything together, and lastly transpile and minify the result?

Sure, that's what many people do today. We see whole stacks of pieces which are orchestrated to work together for specific purposes. They look good, big companies promote them and they lead to fast and beautiful results. Anyone can build a one-page website which looks good in a very short amount of time with very limited programming knowledge. So, actually that's a good thing, right?

It also helps unify the web. People can push certain technologies, browser vendors can optimize for certain big frameworks and the whole world will become a better place. Since a lot of people use these frameworks, lots of tutorial content, and knowledge is made available. Knowing the frameworks will guarantee you a job in the web development industry. We don't have to go back to the old days, where people sat down for long periods of time only to build websites that look weird and were completely different from everything else; those sites were a pain to maintain later on, especially when someone else inherited the code.

Take React as an example. According to the official homepage, React's strength lies in the following core points:

  • Design simple views for each state in your application
  • Declarative views make your code more predictable and easier to debug
  • Build encapsulated components
  • Easily pass rich data through your app and keep state out of the DOM

React is good at breaking down your front-end into better digestible pieces, and separating data from layout.

As a result, you will be able to write a simple page with custom tags and have React inject your actual components, inside them.

Example:

<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>My HTML File</title>
  <script src="react.js"></script>
  <script src="component.js"></script>

  <p><hello-world></hello-world>

React code in component.js will inject the content of <HelloWorld> component into <hello-world>

'use strict';

// This is your component. It can contain the component's state info as properties directly on the class object
class HelloWorld extends React.Component {

  // What HTML to insert into the DOM
  render() {

    return React.createElement('h1', null, 'Hello World!');
  }
};

// Search for the custom element to replace
const ele = document.querySelector('hello-world');

// Finally replace the element
ReactDOM.render(React.createElement(HelloWorld), ele);

Pretty cool, huh? Especially when you have many components, you can put each of them into their own files and have a good overview at the file system level.

Since one can get a pretty nice advantage on the client side, would the same be possible for the server side on Node.js, too? There are frameworks/libraries which try to give similar advantages on the server side. React, for example can render your HTML on the server. React uses a renderToString() method that traverses the tree of your React components, and generates a corresponding HTML string.

Another, more general, framework is Express.JS. With Express, you can define routes and implement the business logic easily.

var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {

  res.send('hello world');
});

// POST method route
app.post('/', function (req, res) {

  res.send('POST request to the homepage');
});

Express truly deserves to be called one of the simplest frameworks to get a webserver with routing up and running.

...But Only With Constrictions

So, where is the catch? Actually, the biggest one is that people start using frameworks and libraries without even thinking about how to solve the problem without them. Often, a solution in VanillaJS is very easy, but people are so used to work with frameworks and libraries that they load a lot of extra code to do very simple things. One of the most iconic troublemakers of this movement is jQuery, which until recently was literally used everywhere, with tons of plugins. On certain question and answer platforms, instead of finding the proper and basic solution, libraries were promoted instead. Here is a nice little joke about it.

But even after thinking about a certain problem, many people want to use a certain library or framework. They could do it in plain JS, but they see the advantages of using the framework. For example, people often build small personal websites. It is great that React splits that problem into smaller problems, but the advantage gained might as well turn into a disadvantage. This is because in order to use the library, a lot of boilerplate code has to be added. Since the code base could have been small to begin with, the splitting of components looks more like a fragmentation of the page, with a lot of code doing only very few small tasks. There is a big overhead in several ways (boilerplate, traffic, logic). Let's take the React example from above. No one would write such code outside of documentation. Any sane person would just write...

<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>My HTML File</title>

  <p><h1>Hello World!</h1>

...and be done with it. As a result, it is important to do a comparison of JS frameworks vs. plain JS, and inspect how different solutions fare in different scenarios.

FrameworkRouting PerformanceTeam
Vanilla21.8k/sec
  • Collaboration depends entirely on the architect
  • Junior Developers might learn a lot
  • New additions to the team have to go through learning the architecture first
Express8.6k/sec
  • Collaboration depends on the business logic architecture
  • New additions to the team, if familiar with the framework, will easily understand how a request is handled
Koa10.7k/secsame as Express
Hapi3.3k/secsame as Express
Spirit + Router21.7k/secsame as Express

(Source)

The chart clearly shows that VanillaJS is the winner and most routing frameworks are very slow. Interestingly though, Spirit can nearly keep up with plain performance, so performance-wise a framework might not even pose a disadvantage. In addition to such routing frameworks, server-side rendering can be implemented. Server-side rendering means that a certain percentage of the HTML is built on the server and then delivered to the browser, ready to be displayed.

While performance might not always be of concern, while working with a team, how individual members can work with the code, sure is. A team usually consists of several developers with different states of knowledge. There might be senior developers, junior developers, and developers in between. In the table above, it says "Collaboration depends entirely on the architect" for VanillaJS, and "Collaboration depends on the business logic architecture" for the rest. The difference between those two is that an application is usually made up of base-code, which enables the whole program to function, and some business-logic, which is code that implements the purpose for which the program is actually created. Which part is more important? Exactly. The business logic!

So, while it is nice to know that someone has already implemented some kind of a base framework, the business logic will always need to have a well structured architecture. Younger developers will tend to use the kind of architecture dictated by the framework. There is a clear line between how the application should be built and how it will result in a working application.

Here's the problem: Frameworks usually dictate an architecture, which might be nice for certain situations, but not for all. So the code might end up messier, bigger and slower than it could have been, if the developer would have spent more time thinking about how the business logic should be structured, depending on the result, and not on what "one" framework's wants.

Even when such thoughts are taken into consideration, the business logic still needs some kind of an adapter to fit into the framework. In the end, it boils down to decisions for a clear architecture, which is optimal for the kind of use case the application should serve, versus a rapid initial development, just because fewer decisions have to be made. Both ways are acceptable, depending on the use case. For example, a prototype might benefit from a rapid initial development with decoupled state, and widgets using a framework everyone understands. But the implementation, for maximum performance, might use its own architecture, or a custom framework and environment for the best performance/resource usage, and minimum headache later on(Source).

One more very interesting point is the context and the size of the project. There are small projects, like a personal website with a portfolio, contact info, and maybe even a blog. However, there are also big projects, like Facebook, with lots of functionalities and reusable parts of the GUI. Different project sizes and scopes would mean different requirements. As stated above, a prototype might benefit from a framework everyone knows and is familiar with, so work can be done rapidly.

For a small website though, building a prototype is not necessary and speed might be quite adequate. As a result, even suboptimal code would be acceptable, and might even be preferred, as it can be done using less code. If it is a small site for a project, it might even be desired, if a few people, maybe in a constantly changing team, have to maintain the website. A big website, however, is different. Big sites usually have big teams of employed people working on them.

React is a library made by Facebook. It is a specialized piece of tech for their particular website. They did not use Angular or anything else, but basically used plain VanillaJS to build something to cater for their needs. For Facebook, React is a VanillaJS implementation. However, for any other site, which is not Facebook, React is not the right tool if it does not try to solve the very same problem as Facebook. It is more like a prison, restricting the developers. The same goes for all other frameworks in existance. Every single one was created for a very specific problem. It does not make sense to use it in a different context, or the mentioned problems, like the need for an adapter for the business logic.

Lastly, most Frameworks you use are quite well done and mostly work as expected.

What About Libraries?

Interestingly enough, some of the mentioned problems are not only true for frameworks, but there are a lot of problems with libraries as well. One of the most well known libraries is jQuery. In some portfolios, it is even equalled with a programming language, which of course is very wrong. So what exactly is so great about jQuery, and other libraries from the same era, like modernizr (a library to find out CSS browser capabilities and react based on the results inside pure CSS)? Those libraries were released between 2006 and 2009, when HTML5 and CSS3 were still drafts. Many browsers implemented HTML4/CSS2 and JS features in different ways. Especially IE7 and IE8 were released, and developers had to support IE5 and IE6. The whole browser landscape was a mess. jQuery was built with interactivity and DOM manipulation in mind. It was a way to unify all approaches and implement things which were added to HTML5 and CSS3 specifications. In order to do so, jQuery implemented many features in its completely own and unique way. It was a blessing and smoothed over all the differences. Today, we don't need $('.my-foo'). We have document.querySelectorAll('.my-foo'). jQuery's .animate() is not useful anymore, either. We have CSS animation and @keyframes. The most current jQuery build is 32kb in size when gzipped. That's 32kb which can be saved, when modern techniques are used.

Another big problem with libraries, like jQuery, is that they have plugins and additions, which promise to solve one of your problems. For example, if your problem is to develop a menu with dropdown, you will easily find a jQuery plugin which does the job. But, as with frameworks, you put a big layer of abstraction into your application, which means you loose performance and resource usage increases drastically. It also means you have to use whatever API the library provides. That means, that not everything is possible and an adapter to the solution might be necessary for certain behavior. All in all, a solution without the library might have been more flexible, smaller in size, more performant (especially important on mobile and embedded) while also being quick to implement and just as well maintained as the overall project. Just think of the following situation: Something goes wrong. An error pops up after months of development, and you cannot figure out why your program does not work any more. Using frameworks or libraries comes with the danger of not understanding the plain JS underneath. In a situation, where you have to start reading the code of some third party software, you need to understand what is happening, and it is not enough to just be able to use the API.

Did you know, it is even possible to combine bad framework behavior with bad library behavior? There is a saying, which goes like "to every joke and every warning, there is a real story". I couldn't put it better than iamdevloper: “So, what will you be using for your homepage carousel?” “I’m using React and jQuery with Bootstrap for styling” “React and jQuery?”. Please, whatever you do, always make sure you know the full capabilities of the tools you use!

Vanilla JS To The Rescue

So, now that you know the tools and their short-comings, how exactly does the term "VanillaJS" fit in there? What is meant by that? Is it some kind of s framework? Is there a website about it? Is it hard to understand what is meant by it? Is it really used by more people than "jQuery, Prototype JS, MooTools, YUI, and Google Web Toolkit - combined"?

Well, YES! The term "Vanilla", when put in front of something intangible (unlike vanilla ice cream), means that the subject is unmodified. You just use the basic product without anything added to it. As for JS, what it means is that you do not use any framework. All you do is use the most basic API functions of the JS interpreter, like setTimeout(). As a result, any website not using a framework has to automatically use VanillaJS, and even when using a framework, VanillaJS can be used to implement certain features or algorithms. Also, frameworks usually do have to use VanillaJS, but a discussion whether that can be counted towards VanillaJS is out of scope for this article.

From the explanation, it is clear, that when not using a framework or library to do the job for you, you will have to fall back to plain old JS, and implement the feature yourself. VanillaJS allows for the full freedom of implementing whatever architecture you want and need. It allows perfect integration with your business logic, the basic server or DOM manipulation logic and API usage for whatever other tools or projects you want to connect. All of the mentioned in a perfectly straight-forward manner. Want to see some magic?

On Hashnode, there was a discussion not too long ago, how to implement autocomplete which only does its thing when the user stops typing for a set time. The answer ranged from "use lodash" to "use <some other library>", because, according to them, plain JS would be tedious and hacky. Say hello to a solid VanillaJS solution with less than 10 (ten) lines of code:

// Get searchbox element
const searchBox = document.getElementById('my-search-box');

const displayAutocomplete = () => {

  // Send box content to server and fetch possible completions
  // Then display them in a neat way next to the search box
  //
  // Not in the scope of this snippet
};

// declare a variable in a high(er) scope to store the timeout ID
var tID = undefined;

// Add a listener to the onKeyUp event of the searchbox
// This is necessary as we only want to _react_ to user input
searchBox.addEventListener('keyup', ev => {

  // If the event fired previously and a timeout ID was stored...
  if (typeof tID !== 'undefined') {

    // ... the timeout should be cleared, since the user is still typing
    clearTimeout(tID);
  }

  // Delay the autocompletion dialog one second
  // and store the timeout ID in the appropriate variable
  tID = setTimeout(displayAutocomplete, 1000);
});

Lodash is ~4 to ~23 kB gzipped, according to the official homepage. The above solution can fit into a 154 bytes (opimized & minified & gzipping), which means the amount of data which has to be transmitted has been reduced by more than 2564%, if you would have used the core Lodash lib with 4kB. AWESOME! By the way, the minified version would look like this:

var t;document.getElementById('my-search-box').addEventListener('keyup',$=>{typeof t!=='undefined'&&clearTimeout(t);t=setTimeout(displayAutocomplete,1e3);})

The result: fewer code has to be sent, fewer code has to be parsed, and executed, and the algorithm uses the standard API, meaning every call is transformed into some very low level compiled function. The gained traffic volume saving, resource saving and speed increment might not be noticable by users for this specific example, but if a developer would have used libraries for every little thing, it might have summed up to a considerable amount of resources. Additionally, the above code is very short, easy, clear and expressive. So anyone reading it will understand what is happening.

Let's go and take a look at one of the most well known question and answer platforms. Searching for the tag "jQuery", there are questions, like "how to filter and remove DOM nodes with jQuery" with answers, like:

$("#offices option:not([data-faculty*='Europe']").remove();

Couldn't one do the same thing without jQuery? Here you go:

document.querySelectorAll("#offices option:not([data-faculty*='Europe']").forEach(ele => { ele.remove(); });

Sure, it looks a bit longer, but basically it does the same thing and, at the very same time, is more transparent. As any developer, even one who doesn't know jQuery, can see that all DOM nodes with the specified selector are effected. Also, you just saved 32kB of gzipped jQuery-library there. On top of that, instead of going through complex jQuery classes and abstractions, you call the very performant browser API directly, which is the most noticable on mobile and embedded.

To sum it up, in the two examples in this section, people were lazy. They used a library to abstract something which, originally, is very easy to begin with, because they did not try to find the VanillaJS solution. They just hoped that some framework must be more simple to make their lives easier. As a result, they have just shoved a lot of data into the users' browsers which they wouldn't even need, with expensive, but unnecessary abstractions. In the worst case, such developers use big frameworks, like React+Redux+whatever, on the server side to serve static content, because they think that the framework will magically improve every aspect, and probably is even needed for today's web to be successful. The only solution is to take a big step away from all the frameworks and libraries, and first try the VanillaJS solution.

Conclusion

Today, we have many great tools. They all try to solve a problem and when you have that very problem, the tool might make your life a lot easier. However, a lot of people hear that tool X is awesome and then misuse it. Even if they like the tool, it is imperative to use the brain. A product, made for production - for example the homepage used by actual users - should always start out with VanillaJS.

However, using VanillaJS does not mean you must not use any third party tools at all. You can always calculate the requirements and if there is a framework which fits 99.999%, it might be used. If you stumble upon a particular micro-problem on your way, you are free to add a nice little library which makes certain things easier for you. Just take a moment and think if whatever you want to do can be done with plain old JS easily. No one wants you to be spartanic and masochistic, but do the math: how much more data volume do you send on the wire and how much does it influence resource usage and performance.

About The Author

Marco Alka is a Full Stack Web Developer at Bosch CI (Corporate Sector Information Systems & Services). He has been building up knowledge autodidactically over several years. As a minimalist, Marco's focus is on performance and architecture. With the accumulated understanding, he helps solve programming problems and answer questions on various topics in different communities. Follow Marco Alka on Hashnode: @maruru!