Back

A Million Times Faster: Million.js vs React

A Million Times Faster: Million.js vs React

This article compares React and Million.js by explaining their features and performance metrics. After this comparison, you can know more specifically which library may go well in your project, especially if it is performance-related.

Performance is one aspect of web development that affects user satisfaction and engagement. In this day and age, users expect instant and smooth interaction, which implies that the responsiveness of a front-end library or framework becomes important. A wrong choice might translate to pretty slow load times and a feeling of frustration on the user’s part, while a good choice will improve responsiveness and favor the expected success of an application.

React has become an important tool of modern web development. It brought a new way of building user interfaces to developers with its component-based architecture and an efficient virtual DOM. Of course, the front-end development growth trend is fast, and new libraries are running for better performance and more efficient yield. One of them is Million.js, which was built to provide more effective speed. It achieved this through optimization in rendering, overhead reduction, and the usage of highly performant algorithms.

Million.js is a new member of the JavaScript library ecosystem. Written by Aiden Bai, it focuses on performance optimization from the very beginning. Unlike classic libraries with high rendering costs and not-so-smart updates, it embeds advanced algorithms to improve virtual DOM operation aspects. The main idea behind it was to reduce overhead and fast in rendering, making it suitable for performance-oriented applications.

One of the central innovations it brings into virtual DOM management involves modern techniques that greatly reduce the time and resources invested in UI updates, making user interface communications more fluid and responsive. Its affinity for efficiency distinguishes it from more established libraries, thereby helping developers write apps that fare well even under heavy loads or state changes.

Even with its relative newness, it quickly captured developers’ interest because it is very appealing for modern web development projects, promising high performance and a smooth, user-friendly development experience. Its impact on the JavaScript ecosystem will likely expand new possibilities for developing fast and efficient web applications. For all details regarding how to get started with Million.js, refer to this article on OpenReplay’s blog.

Detailed Performance Comparison between React and Million.js

Performance is one of the factors that influences a final decision regarding a JavaScript library when building user interfaces with dynamic behavior. This performance comparison will illuminate the question of Million.js versus React, two libraries with different performance stances. Let us dig deeper into how these two libraries work regarding rendering speed, initial load time, update efficiency, memory usage, and Time-to-Interactive.

Rendering Speed

Rendering speed represents how long it takes to refresh and visually display a UI or page after each change. This includes the time required to process and display content on the screen. Rendering speed is one of the key aspects to achieving smooth and responsive user experiences with dynamic web applications when changes are made to the interface frequently.

Million.js employs an advanced virtual DOM and a highly efficient diffing algorithm. This design minimizes the number of updates required and accelerates rendering times, making it one of the fastest JavaScript frameworks according to the JavaScript web frameworks benchmark - official run with a recorded geometric mean of 1.05. Due to the minimalistic approach, there are fewer overheads and more streamlined processes. Optimizing rendering speed gives fast updates and instant visual feedback.

On the other hand, React handles and updates UI using a virtual DOM and a reconciliation process. This means reviewing the new virtual DOM against the previous one for changes. Most of the mature ecosystems, together with several optimizations, benefit from its rendering speed. However, this can be impeded by both the complexity of the component tree and the features in use. Its geometrical mean in the JavaScript framework ranking is 1.4, meaning it is slower in comparison. It should also be noted that it provides several tools, including React.memo, useCallback, and code-splitting, to help in optimizing the performance.

Below is the image of their rendering speeds comparison according to the official run:

image Source: Js-framework-benchmark

Initial Load Time

It is the time a webpage or an application takes to load and display its content to a user for the first time. In this stage, resources are fetched, parsed, and processed, after which the result of this initial view gets returned to the user. It directly impacts the perceived performance and user experience.

Million.js is lightweight and highly performant. It generally results in a faster initial load time, usually because of the smaller bundle size. It focuses on reducing overhead through its lightweight architecture and optimized virtual DOM. With fewer dependencies and a smaller footprint, it tends to have faster loading and execution time.

This is an example that shows its initial load time:

import { block } from "million";

const App = block(() => {
  return <div>Hello, this is Million.js!</div>;
});

render(<App />, document.getElementById("root"));

Output: mi

This code significantly reduces the computational overhead by minimizing JavaScript processing during advanced virtual DOM manipulation, thus achieving a very high performance rate compared to a similar React code. This translates into speed and responsiveness to actions on a user interface.

On the other hand, React has a larger bundle size when compared, leading to longer initial load times. While powerful, this can mean a slower first load than libraries with simpler scopes. Its applications can use code splitting, lazy loading, and other optimizations to make the time-to-initial-render fast. These strategies are not always enough to completely compensate for the large size.

Here is an example:

import React from "react";
import ReactDOM from "react-dom";

function Hi() {
  return <div>Hello, this is React</div>;
}

ReactDOM.render(<Hi />, document.getElementById("root"));

Output: re

Despite its simplicity, the code’s initial load time will be longer due to React’s larger size and additional processing compared to the Million.js code above. In an application where the initial load time is a significant concern, Million.js provides an advantage. However, React’s extensive ecosystem and capabilities might justify its longer load times depending on the project’s requirements.

Update Efficiency

It refers to how effectively a library or framework handles updates to the user interface. This includes how quickly and efficiently changes are reflected in the DOM when the application state changes. Now, let’s compare the update efficiency of the two libraries.

Million.js uses one of the most optimized virtual DOMs, which minimizes the work of updating a real DOM. It reduces unnecessary operations and focuses on direct changes, thus handling updates faster. Applying a change to a virtual and real DOM uses a very simple patch algorithm that guarantees extremely fast updates with very low overhead. Its architecture supports selective rendering, so it will re-render only components whose props have changed. This avoids the performance costs associated with unnecessary re-renders.

To illustrate with a counter:

import { Block } from "million";

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <h1>This is MILLION</h1>
      <h2>You incremented {count} times!</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
 );
}

// Create a block using the App component
const appBlock = Block(App);

const rootElement = document.getElementById("root");
appBlock.render(rootElement);

Output: Untitled video - Made with Clipchamp

Performance test output:

image

In this example, clicking the button increases the count and re-renders. It updates only the parts of the DOM that have changed (the <p> element) and does so very quickly. As seen in the picture, the painting took 64ms. It is to be noted that Chrome DevTool was used to record the code performance, and painting is the time it takes for an update to be displayed. The increment button was continuously clicked 12 times in both outputs.

In React, acting upon updates in elements with the virtual DOM and reconciliation is very efficient. It computes the difference between previous and current virtual DOMs and applies only the necessary changes to the real DOM. It also provides lifecycle methods or hooks in functional components to allow optimization during the update. For instance, shouldComponentUpdate avoids unnecessary re-renders by comparing the current with the next props or state. It also batches updates by default, where a bunch of state changes are grouped and run in one render cycle. That way, the total number of actual DOM updates is reduced with the side benefit of better performance.

For example:

import React, { useState } from "react";
import ReactDOM from "react-dom/client";

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <h1>This is REACT</h1>
      <h2>You incremented {count} times!</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
 );
}

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);

Output: image

Performance test output:

image

In the code above, when you click the button, the count gets updated, and there is a re-render. React also includes a diffing algorithm that is very efficient at updating only elements that change on the DOM. The painting here took 83ms, showing it took more time to display updates.

For this metric, both libraries aim to be performant in handling updates, but Million.js’s lightweight and simple approach is more than helpful for most cases where the minimum overhead and fastest updates are necessary. On the other hand, React offers a wholesome solution that further has functionalities useful for optimizing performance for complicated applications.

Below is an image of the interactive result between their partial update as per the JavaScript official framework benchmark:

image

Here, the Million.js partial update is capped at 49.0 while React is capped at 58.8, showing the former is faster than the latter when their update efficiency is compared, even though the difference is not too big.

Memory Usage

This is a major deciding factor when comparing different JavaScript libraries, especially in applications running without a glitch on low-resource devices. Developers leverage numerous memory optimization techniques to prevent useless allocations and efficient data structures so as to leverage in-built optimizations for the chosen JavaScript library or framework. By closely monitoring memory use, developers can create high-performing and scalable applications. This makes a much better user experience despite the device’s full capabilities.

As previously mentioned, Million.js is designed to be very minimalistic in the bundle size, hence directly correlating to a lower level of memory usage. It uses memory efficiently by using an optimized virtual DOM implementation, a simple patch algorithm, and a simplified virtual DOM that helps reduce memory overhead. It is economical with memory use as it focuses on what to implement as features and abstractions without putting in features that most users probably would not end up using, hence less memory overhead. Also, it has few objects, data structures of lower complexity, and more efficient garbage collection. As a result, it leads to more infrequent and less intense garbage-collection cycles and, therefore, helps keep memory usage low.

React has a full list of features, so at certain times, it might cause extra memory usage by producing a larger bundle and a more involved virtual DOM tree structure. This is because its virtual DOM (and hence, reconciliation) keeps multiple representations of the DOM in memory. This, together with its architecture, can be quite vulnerable to higher memory use than lightweight libraries. It also comes with a tool and technique set in order to enhance the optimization of memory use, principally by hooks like useMemo and useCallback, to be able to bypass unnecessary re-renders and, therefore, memory allocations. These features need to be explicitly coded and put in place by the developer. In other words, its memory usage is high due to its large virtual DOM and state management.

Here is the comparison ratio from the JavaScript framework benchmark:

image

The geometric mean ratio from the picture above indicates that Million.js uses less memory. Therefore, it offers an advantage due to its lower memory consumption in applications that run on devices with limited resources. With its extensive ecosystem and feature set, React might be preferred for more complex applications where the benefits of its additional capabilities outweigh the cost of higher memory usage.

Time to Interactive (TTI)

TTI is one of the biggest performance metrics, which measures the time between when the user starts to load a page and when it becomes interactive. It measures the responsiveness of a page following its loading cycle—like rendering the layout, loading JavaScript, or any other resources. This forms a response to user input, represents the actual user experience, and specifies how fast real users can use the content. The lower the TTI, the more responsive and user-friendly the page is. Ensuring a smooth, seamless experience on your website requires tracking and optimizing TTI.

It has been ascertained that Million.js small bundle size ensures faster initial load times, and that will directly contribute to a shorter TTI. This means that it does the least possible work to update the actual DOM with an optimized virtual DOM, which reduces the time it takes for rendering and makes interactivity faster. It has a direct approach to rendering components with few abstractions and layers, so the overhead during the initial process is low.

React offers a rich feature set with abstractions, which could lead to more initial loading time. Such complexity might make the TTI take longer because more resources must be carried over for the page to become interactive. This makes its virtual DOM and reconciliation process efficient, though a lot more complex, which means component setup and mounting can take slightly longer, affecting the time to interact.

You often see deferred loading and code splitting as techniques applied in React applications to achieve a lower TTI. Loading only the parts of the application that are needed upfront makes the page interact earlier. This requires further configuration and best practices to achieve the best results possible.

Server-Side Rendering (SSR)

This is a technique where the HTML content is created on the server rather than rendering it entirely in the browser. SSR can greatly influence performance, SEO, and user experience by reducing time to first contentful paint and enhancing initial load time.

While Million.js is optimized for efficiency and fast rendering, built-in SSR support is one of the features it lacks. Therefore, custom SSR solutions may have to be engineered or integrated with existing Node.js servers to allow SSR. Notably, its efficiency and minimalism result in a reduced server-side processing time, which in turn allows greater performance with respect to response time and a faster initial load.

React supports server-side rendering quite well, especially through Next.js and ReactDOMServer. Its ecosystem has solutions that handle much of the associated complexity. It contains methods, such as ReactDOMServer, for rendering components into static HTML on the server. This is very well documented and supported, so it’s actually fairly painless to implement SSR with it.

Where SSR is paramount, the matured ecosystem of React and its included tools provide the best, smoother, and more efficient route. However, Million.js still achieves good performance in such cases where simplicity and lightweight design are of importance with customized SSR implementations.

Comparing Other Important metric

Let’s briefly compare the state management features and developer experience of these two libraries:

State Management

React uses useState and useReducer for local component state and Context for global state management, while complex state management is usually done with libraries like Redux, MobX, or Zustand. This makes the ecosystem flexible for developers creating state management solutions according to the needs of their specific projects. This also enables the addition of third-party state-management libraries and tools that have plenty of resources, plugins, and community support.

Million.js does not yet have detailed built-in state management options. It is primarily focused on efficient DOM rendering. Since it does not offer those features, you would have to plug in other state management libraries or solutions.

Support for JSX

JSX (JavaScript XML) is a syntax extension for JavaScript that allows developers to write HTML-like code in JavaScript, which enhances visualization and user interfaces. It allows embedding JavaScript expressions in markup using curly braces {}- thus, it indulges dynamic rendering. It also supports component composition, loops, and passing attributes/children to elements or components. It is a powerful way to efficiently build and manage dynamic features with HTML-like syntax and JavaScript capabilities.

React fully supports JSX, so it is part of the development experience at its core. With its seamless integration, you do not have to take extra configuration steps when it is used in projects. Starting from when you create a new application, it is ready for usage without any additional steps. This is mainly because its tooling and ecosystem have been built around JSX from scratch. It gets good tooling support for the extension since such tooling is well-established. It also comes with complete error correction and warnings for the syntax. Of course, its large ecosystem is also JSX-aware with useful facilities like ESLint, Prettier, and TypeScript, having dedicated plugins and configurations for understanding JSX, which allows them to lint, format, and type-check code. This makes the development process smoother and more consistent, with robust support across different development tools.

JSX is not natively supported in Million.js since its prime focus is only on being an efficient virtual DOM for highly optimized rendering rather than a comprehensive UI framework like React. To use the extension, the developer must set up the environment with tools like Babel or a similar Transpiler. This setup involves configuring the Transpiler to recognize and transform the syntax into standard JavaScript that Million.js can work with. While you can use the extension with this library, the development experience is less clean and smooth since extra configuration for handling the extension by other libraries or tools adds more complexity. That could be a good trade-off if you want to achieve the performance it allows but still need the readability and syntactical effectiveness of JSX.

Developer Experience

The learning curve with React is quite average. One has to learn about components, hooks, and context, but once these are mastered, it is a powerful and flexible framework. In addition, it has broad documentation, a large community, and a diversity of development tools like React DevTools, which improve the development experience. A large community and ecosystem also mean many tutorials, solutions to common problems, and third-party tools are available.

Million.js is relatively new and has less mature documentation and community support, making the learning curve might be steeper. On the other hand, it is designed to be easier and more performant in some cases due to a focus on rendering efficiency. That may simplify development in some scenarios but limit flexibility. Because it is new, it does not have such thorough documentation or community support, which could make a difference in the developer experience.

Conclusion

Million.js is an alternative for any performance-conscious developer, primarily for applications dealing with dynamic content and large datasets. In contrast, the mature ecosystem, extensive tooling, and established community support give React flexibility and power in every kind of application. So, the choice between the two libraries will depend on the project specifications.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay