The frontend ecosystem is saturated with a plethora of frameworks, and it seems like new ones are being released daily. While many of these frameworks may not be as widely used as the more popular ones, some of them offer unique approaches that might pique your interest.
In this article, we will introduce ArrowJS, a JavaScript tool for building reactive user interfaces, and compare its approach to those of popular UI frameworks such as React and Vue.js. We’ll explore how ArrowJS differs from these conventional frameworks and what sets it apart.
Jump ahead:
To understand the concepts and examples shared in this article, you should have the following:
ArrowJS is an experimental tool for building reactive user interfaces using pure JavaScript. It uses modern JavaScript features, such as template literals, modules, and proxies, to implement its templating structure, observable data, and declarative/reactive DOM rendering capabilities.
The creator of ArrowJS believes it’s not necessary to have a complex framework to create impressive and performant user interfaces on the web because JavaScript has evolved to be powerful enough to handle these tasks natively.
As a result, ArrowJS has no dependencies, no Virtual DOM, no build tool, and no special templating language. It is also very lightweight, weighing less than 3kB (min+Gzip). This makes it ultra-fast compared to frameworks like React and Vue, which have comparable features.
Unlike many other JavaScript frameworks, ArrowJS does not use a build tool or a templating language. This means there is no need for compilation or conversion during build time, leading to better performance.
In contrast, React uses JSX as its templating language, which must be compiled and converted into native JavaScript render functions before it can be run in the browser.
There are several reasons to consider using ArrowJS, including the following key benefits:
We’ll dive into each of these benefits a little later in this article.
There are three possible ways to set up an ArrowJS application: using a package manager, installing locally, or via CDN. Let’s take a look at each option.
As previously mentioned, ArrowJS does not include a build tool or bundler by default. However, if you want to use a package manager like npm or Yarn to bootstrap your project and take advantage of features like Hot Module Reloading, you can use a tool like Vite or Snowpack to bundle your project.
Use the following commands to install ArrowJS via npm or Yarn:
//npm npm install @arrow-js/core //Yarn yarn add @arrow-js/core
To install ArrowJS locally, you’ll need to download the ArrowJS package from GitHub and add it to your project directly. Then, reference it in your script module like so:
<script type="module"> import { reactive, html } from '/js/arrow.js'; //your app’s code goes here </script>
Installing ArrowJS via CDN is as simple as adding the following import statement to your script:
<script type="module"> import { reactive, html } from npm install @arrow-js/core'https://cdn.skypack.dev/@arrow-js/core'; // Start your app here! </script>
ArrowJS is based on two composition expressions: static and reactive. However, the tool’s creator believes that reactivity should be an option, not a requirement. Therefore, ArrowJS is static by default and reactive by choice and only provides three functions for handling the templating and reactivity capabilities:
reactive
(r
)watch
(w
)html
(t
)reactive
functionThe reactive
function, or r
in shorthand, is an ArrowJS function that converts generic data objects into observed data objects. This function monitors the object for changes and automatically updates any related templates or dependencies as needed.
Using the reactive
function is straightforward; simply pass the object you want to monitor as an argument to the reactive
function call, reactive(object)
, like so:
import { reactive } from '@arrow-js/core' const users = reactive({ name: “John”, age: 25 })
In this example, suppose we have a variable that depends on any of the object properties within the reactive data. When the object properties change, it is expected that the value of the dependent variable will be updated accordingly:
import { reactive } from '@arrow-js/core' const users = reactive({ name: "John", age: 25 }) users.name = "Mike" const data = user.name; console.log(data); //logs 'Mike' to the console
However, ArrowJS offers $on
and $off
methods that allow us to observe changes to reactive properties.
The $on
method takes in two arguments: the name of the property and a callback function. This method observes the specified property for changes and runs the callback function whenever a change occurs:
import { reactive } from "@arrow-js/core"; const users = reactive({ name: "John", age: 25, }); const nameFunc = (value) => { console.log(`name changed to ${value}`); const data = value; } users.$on("name", nameFunc); setTimeout(() => { users.name = "Mike"; }, 2000); //"name changed to Mike" will get logged to the console two seconds after initialization
In the example above, the $o
n callback function will run when the name property changes.
The $off
method, on the other hand, is used to remove the attached callback from the $on
method.
For example, if we want to stop the reactive
function from observing the name
property, we would use the following code:
const users = reactive({ name: "John", age: 25, }); const nameFunc = (value) => { console.log(`name changed to ${value}`); const data = value; }; users.$on("name", nameFunc); setTimeout(() => { users.name = "Mike"; }, 2000); users.$off("name", nameFunc); //We stopped observing 'name' with $off // so changing users.name will not log anything
watch
functionAlternatively, we can use the ArrowJS watch
function to track and untrack reactive properties. The watch
function, w
for short, is an inbuilt ArrowJS function, just like the reactive
function. But, unlike the r
function, watch
takes in a function and tracks any reactive dependency of that function:
import { reactive, watch } from "@arrow-js/core"; price: 25, tax: 10, }); function total() { console.log(`Total: ${data.price + data.tax}`); } setTimeout(() => { data.price = 35; }, 2000); watch(total); //"Total: 45" will be logged to the console two seconds after initialization
The watch
function will monitor the price
and tax
reactive properties for any value changes. It also activates the $on
observer for these properties and automatically detects when they are no longer being used by the function, at which point it will call the $off
observer and stop tracking the property.
html
functionThe ArrowJS html
function, t
for short, is used for creating and mounting Arrow templates to the DOM. It uses tagged template literals to render contents declaratively.
To create an ArrowJS template, you prefix the tick mark with the html
keyword or its shorthand t
, and add the elements to be rendered.
For example, the keyword or its shorthand followed by an opening tick (html'
or t'
) signals the start of the template, while a closing tick signals the end of the template.
We can then add the elements to be rendered within the ticks, like this: html`elements to be rendered`
or t`elements to be rendered`
Here’s an example:
import { html } from '@arrow-js/core' const appElement = document.getElementById('app'); const template = html`<p>Hello World</p>` template(appElement)
In the above code, we’re referencing an element from the DOM and mounting our template to it.
Let’s take a closer look at some of the features of ArrowJS and see how they compare to those of React and Vue.
Unlike Vue, React, and most other JavaScript frameworks, ArrowJS does not use a component-based approach. Instead, it relies on functions. But since JavaScript components are essentially functions under the hood, both approaches are actually very similar.
These frameworks differ in the kind of templates they encapsulate and their overall structure. For instance, Vue uses a single-file component structure, in which a component’s template, logic, and styling are all contained within a single .vue
file:
<template> <!-- html markup --> </template> <script> // JavaScript code </script> <style> /* CSS styles */ </style>
In contrast, ArrowJS uses standard JavaScript script modules and native code and functions:
import { r, t } from "@arrow-js/core"; const appElement = document.getElementById("app"); // JavaScript code const user = r({}); const template = t` <div class="container"> <!-- html markup --> </div> `; template(appElement);
At first glance, it may seem that ArrowJS is not very different from Vue, as both frameworks encapsulate their templates and logic within a single module. However, ArrowJS uses template literals (a native JavaScript feature) to interpolate expressions and directly render elements to the DOM. Meanwhile, Vue relies on the virtual DOM, an abstraction of the actual DOM.
Another notable difference between ArrowJS and other frameworks is the syntax for binding event listeners to DOM elements. The tool uses the @
symbol, followed by an event name, to denote an event listener:
const clickHandler = () =>{ console.log("clicked"); } const template = t` <button @click="${clickHandler}">Click</button> `;
This will automatically be translated to an equivalent expression:
document.getElementById("btn").addEventListener("click", () => { console.log("clicked"); });
In contrast, React and Vue use the camelCase naming convention and directives for events when binding event listeners to elements:
//React const MyButton = () => { handleClick = () => { // do something when the button is clicked }; return <button onClick={handleClick}>Click me</button>; }; Vue <template> <button v-on:click="handleClick"> Click me </button> </template> <script> export default { methods: { handleClick() { // do something when the button is clicked } } } </script>
Reactivity is a programming paradigm that allows us to adjust to change declaratively. This is merely a concept, not a default feature of programming languages. However, It can be implemented using different tools and technologies, depending on a given system’s specific requirements and constraints.
In JavaScript, reactivity can be implemented using frameworks and libraries such as React and Vue, which are designed for building reactive user interfaces. These frameworks use the virtual DOM to handle reactivity, updating it to reflect the latest data when there is a change. The framework then compares the virtual DOM with the real DOM to determine which parts of the UI need to be updated.
Given that ArrowJS uses native JavaScript, you may be curious about how it handles reactivity. The solution is a custom dependency class.
Reactivity in JavaScript can be implemented using a dependency class that takes in two properties: a value getter and a callback function. The value getter is a function that depends on multiple variables or dependencies to obtain its values.
Whenever the value of a dependency changes, JavaScript will automatically call the callback function and compare the current value to the previous value.
This is how ArrowJS uses the reactive
and watch
functions to handle reactivity under the hood. And, because ArrowJS doesn’t rely on mechanisms like the virtual DOM, its effects are instantaneous.
Another thing to consider is the conciseness of reactive implementation in ArrowJS compared to other frameworks. For instance, the code below demonstrates how to implement reactivity in a React component that shows a user’s name
and age
:
import { useState, useEffect } from 'react'; function ReactiveComponent() { // Create a state variable called "user" to hold the reactive data const [user, setUser] = useState({ name: 'John Doe', age: 32 }); // Use the useEffect hook to specify a function that will be executed // whenever the user's data changes useEffect(() => { // Update the UI to reflect the latest user data // (e.g., update the name and age displayed on the screen) }); // Return the JSX for the component, which includes the current // user's name and age return ( <div> <p>Name: {user.name}</p> <p>Age: {user.age}</p> </div> ); }
Here’s the same implementation in ArrowJS:
import { r, html } from "@arrow-js/core"; const appElement = document.getElementById("app"); // Create a state variable called "user" to hold the reactive data const user = r({ name: "John Doe", age: 32 }); // Dom template which includes the current // user's name and age const template = html` <div> <p>Name: ${user.name}</p> <p>Age: ${user.age}</p> </div> `; // Map template to Dom template(appElement);
One aspect that these frameworks share is how they render declarative data properties in the template. React uses a placeholder indicated by single curly braces to insert reactive content and variables into the string, like so:
<div> <p>Name: {user.name}</p> <p>Age: {user.age}</p> </div>
In contrast, Vue uses double curly braces, commonly known as the mustache syntax, to insert dynamic values into a string. For example, the above template would be rendered in Vue as follows:
<div> <p>Name: {{user.name}}</p> <p>Age: {{user.age}}</p> </div>
Since ArrowJS uses native JavaScript to render elements, it has access to the special placeholder syntax provided by template literals. These placeholders, called template tags, are indicated using the ${}
syntax and are used to insert dynamic values into the template at runtime. This allows ArrowJS to create templates that include dynamic data that gets updated whenever the data changes.
Template tags are used to insert the result of a JavaScript expression into the template. To create a template tag, you simply place an expression inside the ${}
placeholder. When the string is evaluated, the expression is evaluated, and its value is inserted into the template in place of the placeholder:
const template = html` <div> <p>Name: ${user.name}</p> <p>Age: ${user.age}</p> </div> `;
As you can see, the syntax for template tags is similar to that used in React and Vue, except for the $
symbol.
Template tags provide a simple and concise way to insert dynamic values into the template. These tags can be combined with other language features, such as arrow functions and destructuring, to create clear and easy-to-read code.
In this article, we introduced ArrowJS, discussed its many benefits, and compared its features to those of React and Vue. ArrowJS is still in an experimental stage, but is proving to be a powerful tool for building fast, reactive user interfaces with native JavaScript.
Building UIs with ArrowJS offers several advantages over traditional methods, including improved performance and easy integration with existing code. The use of native JavaScript makes ArrowJS a valuable addition to any developer’s toolkit.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]