OpenReplay
Navigate back to the homepage
BLOG
Browse Repo
Back

Solid vs React - the Fastest VS the Most Popular UI Library

Arek Nawo
June 28th, 2021 · 6 min read

React isn’t known for being a performance champion. Instead, it excels mainly by having the biggest mindshare and ecosystem. Sure, both come from React’s leadership in the UI frameworks space and being one of the first in the game, but the point still stands.

This, however, leaves room for other frameworks to compete and beat React on the performance front - such as Solid.

Solid is a declarative JS UI library, combining React’s JSX-centric and TypeScript-friendly approach with a compiler and deep optimizations to deliver vanilla JS levels of performance at no cost of the development experience.

So, how those 2, similar yet different UI libraries compare? Let’s dive deep and analyze ecosystems, docs, performance, and more!

Community and Ecosystem

We’ll start with less technical aspects of UI libraries.

Community and ecosystem are something that React wins hands down. No niche library can compare. That’s mostly why and how React continues to reign supreme.

However, at this point, React dominance in the ecosystem category is well-known, so it’s more interesting to see the other side of the coin.

At over 6K GitHub stars, 69K monthly NPM downloads, and over 700 Discord members, Solid has a decently-sized community. Recently reaching v1, Solid has been over 3 years in open-source development, with over 40 contributors. But, sadly, this doesn’t translate 1-to-1 for the ecosystem.

Solid has an impressive collection of first-party tools developed by its creator - Ryan Carniato - and other contributors. There you’ll find Solid equivalents of some popular libraries from other ecosystems, like Solid Transition Group, Solid Refresh (for Hot Module Reloading - HMR), Solid App Router, and more!

With that said, the situation looks different when looking at third-party tools. There are still some interesting libraries like Solid wrapper around Heroicons or Lume - a toolkit for interactive 2D and 3D experiences, but that’s about it. Still, with an already good first-party collection and numerous framework-independent tools, developing production-ready apps with Solid shouldn’t be a problem.

Documentation and Resources

A great ecosystem inspires the creation of a vast amount of resources like videos, articles, podcasts, and others. So, it’s no surprise that React also wins in this category.

With new resources popping up seemingly all the time and a, sometimes, outdated but good documentation page, React plays in a different league.

However, like in the previous case, Solid’s position is really interesting. Its unique properties and performance-centric approach inspire many developers to write and make videos about it.

Solid’s creator is also very active in this space, writing many blog posts about his research and findings, as well as some behind-the-scenes of Solid’s development and optimizations.

As for the documentation, Solid also has it well-covered, with its high-quality docs only recently moving from GitHub Markdown files to a proper landing page.

Performance

It’s not like performance isn’t important for React - it is. That’s why it was one of the first frameworks to start the Virtual DOM craze and why React Hooks were introduced just a few years ago. But, unfortunately, React’s legacy and focus on clean architecture limits its performance potential.

As one of the oldest VDOM UI libraries, React has a hard time enjoying the benefits of modern advancements and deep optimization.

Granular Reactivity

Some might argue that React’s relatively poor performance (it’s still plenty-fast for many apps) is due to Virtual DOM and prioritization of development experience, i.e., clarity over complexity. To counter the first argument - there’s React-like Inferno. For the second one - there’s Solid.

Currently, I view Solid as the peak balance between development experience and performance, but that speaks nothing of what’s going under the hood.

Solid author dives deep into why Solid’s so fast in one of his blog posts, but to summarize it - it all comes down to granular reactivity. Solid is so fast, thanks to fine-grained updates and the right balance of optimizations.

Optimizations

Compiling, DOM optimization, reactivity, and optimal API - they’re all things you’ve heard before, just most likely not in one place.

DOM optimization is very much present in React with the help of VDOM. However, due to its additional overhead and complex, sub-optimal API (especially before Hooks), its performance takes a hit.

On the other hand, Compiling and reactivity is something you most likely heard in Svelte - the “magically-disappearing framework”. While the compiler does increase the optimization level and thus performance, it doesn’t magically disappear the overhead of components, becoming increasingly slower as the application grows.

Lastly, Vue is an example of both reactivity and good API; however, it also leverages VDOM, neglecting most of its performance benefits due to the additional overhead.

With compilation, fine-grained updates, a declarative approach, reactivity, single-run components, API optimized for minimal overhead, and tons more optimizations, Solid can achieve a no mean fit of bringing all those advancements under one library with unseen levels of performance.

Benchmarks

As with all frameworks, synthetic benchmarks don’t tell the whole story but serve as a good point of comparison. From all available, I find the JS Framework Benchmark the best. It combines results of a standardized test from tens of UI frameworks, is open-source, and has a great, up-to-date comparison table available.

So, the following are the performance tests, comparing Solid to some other popular frameworks mentioned in this post, as well as a vanilla JS implementation:

Performance benchmark

You can see that in general, Solid’s at the top, being about 5% slower than vanilla JS. Compare that to React, which is at best almost 100% slower!

Now, it’s worth noting that aside from Solid, there are 2 faster “UI frameworks” included in the benchmark but not on the table above. These are Mikado (actually a template engine) and doohtml (marked with a note about using manual DOM manipulation), so the point still stands - Solid is the fastest JS UI library!

Aside from performance, the benchmark also measures startup time, which Solid also excels at:

Startup benchmark

However, the most interesting results are probably in the memory usage tests:

Memory usage benchmark

You can see that Solid still manages to beat other UI frameworks in these tests, but it’s a bit further from vanilla JS and even other frameworks not included in the comparison than in the other benchmarks.

What you see here is Solid striking a balance between CPU load and memory usage. Using memory efficiently yet not sacrificing performance for it, can lead to impressive results. If you look at the full performance comparison, you’ll see that libraries that utilize less memory almost always come with lower performance.

With that said, Solid’s result of utilizing only about 26% more memory than vanilla JS implementation while achieving such levels of performance is still very impressive.

Development Experience

The cherry on top of Solid’s performance results is the development experience that comes with it. If we were just focused on raw performance, we could easily go back to the roots of plain HTML, CSS, and JS - but that’s not what Solid is about.

With Solid, you get the best of both worlds - performance, but also declarative approach, JSX, TypeScript, and a lot of React-like features like Fragments, Context, Portals, Suspense, and many more!

Solid also comes with an optimized API that will feel similar if you’ve worked with React Hooks.

For comparison, here’s a quick TODO app done with React and TypeScript:

1import { FormEvent, KeyboardEvent, useState } from "react";
2import { render } from "react-dom";
3
4interface TODO {
5 id: string;
6 text: string;
7 done: boolean;
8}
9
10const App = () => {
11 const [input, setInput] = useState("");
12 const [todos, setTodos] = useState<TODO[]>([]);
13 const done = todos.filter((todo) => todo.done);
14 const notDone = todos.filter((todo) => !todo.done);
15 const createTodo = (text: string) => {
16 setTodos([...todos, { id: `${Math.random()}`, text, done: false }]);
17 };
18 const removeTodo = (todoId: string) => {
19 setTodos(todos.filter((todo) => todo.id !== todoId));
20 };
21 const setTodoDone = (todoId: string, done: boolean) => {
22 const newTodoList = todos.map((todo) => {
23 if (todo.id === todoId) {
24 return { ...todo, done };
25 }
26 return todo;
27 });
28 setTodos(newTodoList);
29 };
30 const addTodo = () => {
31 if (input) {
32 createTodo(input);
33 setInput("");
34 }
35 };
36 const onInput = (event: FormEvent<HTMLInputElement>) => {
37 const target = event.target as HTMLInputElement;
38
39 setInput(target.value);
40 };
41 const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
42 if (event.key === "Enter") {
43 addTodo();
44 }
45 };
46
47 return (
48 <div>
49 <input value={input} onInput={onInput} onKeyDown={onKeyDown} />
50 <button onClick={addTodo}>Add</button>
51
52 <div>
53 <b>Todo:</b>
54 {notDone.map((todo) => {
55 return (
56 <div key={todo.id}>
57 {todo.text}
58 <button onClick={() => setTodoDone(todo.id, true)}>Done</button>
59 </div>
60 );
61 })}
62 </div>
63
64 <div>
65 <b>Done:</b>
66 {done.map((todo) => {
67 return (
68 <div key={todo.id}>
69 {todo.text}
70 <button onClick={() => removeTodo(todo.id)}>Remove</button>
71 <button onClick={() => setTodoDone(todo.id, false)}>Undo</button>
72 </div>
73 );
74 })}
75 </div>
76 </div>
77 );
78};
79
80render(<App />, document.getElementById("app"));

And here’s the same app recreated in Solid:

1import { For, render } from "solid-js/web";
2import { createMemo, createSignal } from "solid-js";
3
4interface TODO {
5 id: string;
6 text: string;
7 done: boolean;
8}
9
10const App = () => {
11 const [todos, setTodos] = createSignal<TODO[]>([]);
12 const [input, setInput] = createSignal("");
13 const doneTodos = createMemo(() => todos().filter((todo) => todo.done));
14 const undoneTodos = createMemo(() => todos().filter((todo) => !todo.done));
15 const createTodo = (text: string) => {
16 setTodos([...todos(), { id: `${Math.random()}`, text, done: false }]);
17 };
18 const removeTodo = (todoId: string) => {
19 setTodos(todos().filter((todo) => todo.id !== todoId));
20 };
21 const setTodoDone = (todoId: string, done: boolean) => {
22 setTodos(
23 todos().map((todo) => {
24 if (todo.id === todoId) {
25 return { ...todo, done };
26 }
27 return todo;
28 })
29 );
30 };
31 const addTodo = () => {
32 const currentInput = input();
33 if (currentInput) {
34 createTodo(currentInput);
35 setInput("");
36 }
37 };
38 const onInput = (event: InputEvent) => {
39 const target = event.target as HTMLInputElement;
40
41 setInput(target.value);
42 };
43 const onKeydown = (event: KeyboardEvent) => {
44 if (event.key === "Enter") {
45 addTodo();
46 }
47 };
48
49 return (
50 <div>
51 <input value={input()} onInput={onInput} onKeyDown={onKeydown} />
52 <button onClick={addTodo}>Add</button>
53 <div>
54 <b>Todo:</b>
55 <For each={undoneTodos()}>
56 {(todo) => {
57 return (
58 <div>
59 {todo.text}
60 <button onClick={() => setTodoDone(todo.id, true)}>Done</button>
61 </div>
62 );
63 }}
64 </For>
65 </div>
66 <div>
67 <b>Done:</b>
68 <For each={doneTodos()}>
69 {(todo) => {
70 return (
71 <div>
72 {todo.text}
73 <button onClick={() => removeTodo(todo.id)}>Remove</button>
74 <button onClick={() => setTodoDone(todo.id, false)}>
75 Undo
76 </button>
77 </div>
78 );
79 }}
80 </For>
81 </div>
82 </div>
83 );
84};
85
86render(() => <App />, document.getElementById("app"));

On the first look, you’d be forgiven to think they’re both the same! Only the second look uncovers small differences like the function calls used to access signals (Solid’s state primitives) values, the createMemo() function, or the <For/> loop component.

The first two are products of Solid’s approach to components. Remember that, unlike React components, Solid’s are called only once. That’s why you need to wrap the signal value in a function and other derived, “computed” values in createMemo(). In Solid, updates are separate from components, which only serve a role of code organization.

As for the <For/> component - it’s yet another performance optimization. Like many other flow control components in Solid, <For/> allows the library to better control and optimize the rendering compared to the traditional .map() method. Of course, you’re still free to use it, but <For/> will provide better performance.

So, to summarize, Solid provides development experience on the same level as React - with most of its bells and whistles included. After adjusting to the different way the components work, you should have no problem developing any app.

Open Source Session Replay

Debugging a web application in production may be challenging and time-consuming. OpenReplay is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder. OpenReplay is the only open-source alternative currently available.

OpenReplay

Happy debugging, for modern frontend teams - Start monitoring your web app for free.

Bottom Line

So, if we were to summarize the compared categories, it’d seem that it’s a tie. React wins the ecosystem, Solid wins the performance, and resources and development experience are a tie. In this approach, the final decision comes down to whether you value more the ecosystem or performance.

Personally, I see great potential in Solid. With appropriate momentum and existing framework-independent libraries, the ecosystem can be built over time, while such a large performance gap can’t be closed easily. If you were looking for more performance or simply something to play around with and learn new concepts - give Solid a shot. I think you won’t regret it!

More articles from OpenReplay Blog

The Art of Writing Good Code Comments

Writing code comments is easy, adding value through them is definitely not. Learn what makes a great code comment with this insightful article.

June 28th, 2021 · 4 min read

Forever Functional - Chaining Calls for Fluent Interfaces

Learn how to create APIs that other developers love to use through a technique called Fluent Interface

June 25th, 2021 · 5 min read
© 2021 OpenReplay Blog
Link to $https://twitter.com/OpenReplayHQLink to $https://github.com/openreplay/openreplayLink to $https://www.linkedin.com/company/18257552