DEV Community

Cover image for SolidJS vs React: I've Built the Same App On Both Libraries.
Oğuzhan Olguncu
Oğuzhan Olguncu

Posted on • Updated on • Originally published at ogzhanolguncu.com

SolidJS vs React: I've Built the Same App On Both Libraries.

SolidJS has gained lots of popularity lately due to having a close relationship with React. It has a declarative nature like React, useState and useEffect like hooks, JSX, ContextAPI, Portals, Error Boundaries. And it gets even better; Solid is much faster in terms of execution and has a lot smaller bundle size. Because it does not carry the burden of Virtual DOM, which means SolidJS uses real DOM instead. When your state changes, SolidJS updates only the code that depends on it.

I built the same app with minimal dependencies, Axios for fetch requests and TailwindCSS for styling. Thanks to core API similarities in both libraries. Before creating this app, I haven't yet had a chance to try Solid. So, I built the app as if I'm using React. Beware, this article does not aim to teach React, or Solid only tries to point out the differences and similarities in both libraries. Let's get started.

SolidJS App Github Link
ReactJS App Github Link

React

const fetchEpisodes = async (optionalUrl?: string) =>
  axios.get<EpisodeResponse>(optionalUrl ?? 'https://rickandmortyapi.com/api/episode');

const App: FC = () => {
  const [episodes, setEpisodes] = useState<EpisodeResponse>();
  const [ref, inView] = useInView({ triggerOnce: true });

  const fetchMoreEpisodes = async () => {
    //Fetching episodes with axios
  };

  useEffect(() => {
    if (inView === true) fetchMoreEpisodes();
  }, [fetchMoreEpisodes, inView]);

  useEffect(() => {
    fetchEpisodes().then((res) => setEpisodes(res.data));
  }, []);

  return (
    <div className="flex justify-center items-center flex-col p-10">
      <h2 className=" font-medium text-4xl my-5">Rick and Morty</h2>
      <div style={{ width: '1000px' }}>
        {episodes?.results.map((episode, index) => (
          <EpisodeWrapper
            episode={episode}
            key={episode.name}
            viewRef={index === episodes.results.length - 1 ? ref : undefined}
          />
        ))}
      </div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Solid

const fetchEpisodes = async (optionalUrl?: string) =>
  axios.get<EpisodeResponse>(optionalUrl ?? 'https://rickandmortyapi.com/api/episode');

const App: Component = () => {
  const [episodes, setEpisodes] = createSignal<EpisodeResponse>();

  const fetchMoreImages = async () => {
    //Fetching episodes with axios
  };

  const handleScroll = () => {
    if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
      fetchMoreImages();
    }
  };

  createEffect(() => {
    window.addEventListener('scroll', handleScroll);
  });

  onMount(async () => {
    setEpisodes((await fetchEpisodes()).data);
  });

  onCleanup(async () => {
    window.removeEventListener('scroll', handleScroll);
  });

  return (
    <div class="flex justify-center items-center flex-col p-10">
      <h2 class=" font-medium text-4xl my-5">Rick and Morty</h2>
      <div style={{ width: '1000px' }}>
        <For each={episodes()?.results} fallback={<p>Loading...</p>}>
          {(episode) => (
            <div>
              <EpisodeWrapper episode={episode} />
            </div>
          )}
        </For>
      </div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Apart from some syntactic differences they are pretty much the same. In Solid we use useSignal hook instead of useState hook. Only difference between these hooks, in useState we can directly call the episodes, but in useSignal we have to invoke it just like a function, because it's a function. If we are using Typescript we can give generic type to our signal just like we do in React.

In React we call our API's in useEffect to supply inital data for states. But, in Solid we can either call lifecycle method called onMount or you can ditch, onMount and use createResource hook. This hook works like a custom fetch — useFetch — takes a function and returns a promise, loading and error status. But, for the sake of ease
I'll go with onMount.

To handle side-effects in Solid we have a hook called createEffect this particular hook quite similar to useEffect but it has some quirks. Instead of taking dependencies manually
it automatically binds itself to state inside that causes changes. Example:

function Counter() {
  const [count, setCount] = createSignal(0);
  const increment = () => setCount(count() + 1);

  createEffect(() => {
    console.log(count()); // Logs count every time it changes
  });
  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Going back to our original example. So, we want to run handleScroll each time person scrolls. We create createEffect and call our event listener. That's it. For the return part, in React we generally use map to iterate over the state, but it Solid we have a built in option called For. It's actually a component which receives each in our case it's episodes state and fallback option to show loading or anything you want. And, good part is you don't have to deal with keys in Solid it automatically handles it for you.

By the way, you can pass props just like you pass props in React everything is the same.

Benchmarks

Benchmarks criteria will be performance profiling in Chrome Dev Tools and final bundle sizes. Let's start with Performance profiling. The performance tab shows an overall breakdown of CPU activity into four categories:

  • Loading: Making network requests and parsing HTML
  • Scripting: Parsing, compiling, and running JavaScript code, also includes Garbage Collection (GC)
  • Rendering: Style and layout calculations
  • Painting: Painting, compositing, resizing and decoding images

benchmark-react-solid

The left side is React, and the right is Solid. As you can see Scripting part is almost 3x faster, Rendering almost 2x faster, Painting part is abnormally faster.

If we went down a level deep on the scripting part, we see why.

React

rect-performnce

Solid

solid-performnce

React first makes a Function Call which evaluates and commits VDOM into DOM, then makes the XHR calls. Since Solid does not have to deal with VDOM to DOM, it skips that part and starts requests right away. By the way, if you are wondering about what Functional Call and XHR Load, means you can check this site Event References.

Bundle sizes of apps:

React

rect-bundle

Solid

solid-bundle

Conclusion

SolidJS definitely doing some things or maybe most of the things better than React, but in my humble opinion, the biggest problem for Solid is the ecosystem. React has an enourmous ecosystem it has components, hooks, patterns for everything. Think something and try serching that thing in npm, and I bet you will find something regarding your needs. Right now Solid's selling point is being fast. In benchmarks it says
it is quite close to vanilla JS.

It is close to vanilla JS, but we are missing the key thing here. People are not going with React because it is fast, and people even know it isn't. They are going with React because of the massive community and tooling ecosystem around it. But I believe that SolidJS has a bright future and, as the community gets bigger and bigger, it'll be even better.

SolidJS App Github Link
ReactJS App Github Link

Discussion (3)

Collapse
ryansolid profile image
Ryan Carniato

I don't think anyone has any illusions on the ecosystem side of things. This is the first concern always. The performance is impressive, but one can always think of ways to wrestle out even more performance if desired.

The key value of Solid is that it can take the tools, syntax, and APIs that you are familiar with, take the flexibility of runtime javascript and composition, and embrace a reactive execution model. This example was pretty much identical on both sides, but as components scale in complexity React Hooks get trickier.

People have been enjoying this in Vue and Svelte for some time now, but Solid has brought that simpler model to React's structured philosophy. The result is something that is performant and requires less logic, but also with explicit control and transparency we love from React.

Collapse
ezrabowman profile image
Ezra Bowman

I’ve been experimenting with Svelte for a while now. Svelte probably has more of a community around it than Solid, but obviously still a far cry from the size of the React community. I’d be interested in seeing the performance difference between Solid and Svelte. Overall I think these frameworks that are faster ultimately will win out, but it will take a while.

Collapse
lexlohr profile image
Alex Lohr • Edited on

To be fair, this is comparing but one use case. If you are dependant on certain amenities, like material UI, input mask, or custom select handlers, you're currently out of luck in Solid. There are also no browser extension dev tools.

On the other hand, you can use styled components, tailwind or similar css frameworks, or create your own components. The current lack of libraries also means that the one you're searching for is not buried under 1000 other mediocre ones. And since components are evaluated only once and only effects and memos run on change, you can use the browser dev tools all right. Not to forget we're catching up fast.

Also, react is based on immutable reactivity, meaning that changes have to propagate from the root to the top, whereas Solid allows for granular reactivity, meaning changes only cause effects to re-run when and where they occur without parent components ever noticing unless tracking the value themselves.

Last not least, the community around Solid is a big part of what makes it great. If you join discord, chances are that your questions will be directly answered by the developers. Also, the project is open to PRs without going through a lot of paperwork first (thanks, Facebook).