DEV Community

Cover image for Solidjs and React.js - Difference and Comparison
Rahul Sharma
Rahul Sharma

Posted on • Edited on

Solidjs and React.js - Difference and Comparison

Solidjs: Solid is a declarative JavaScript library for creating user interfaces. Instead of using a Virtual DOM, it compiles its templates to real DOM nodes and updates them with fine-grained reactions.

React: React is a JavaScript library for building user interfaces. It uses Virtual DOM to efficiently update and render just the right components when your data changes.

Key features of Solidjs:

  • Fine-grained updates to the real DOM
  • Render-once mental model: your components are regular JavaScript functions that run once to set up your view
  • Automatic dependency tracking: accessing your reactive state subscribes to it
  • Provides modern framework features like JSX, fragments, Context, Portals, Suspense, streaming SSR, progressive hydration, Error Boundaries, and concurrent rendering.

Key features of React:

  • Virtual DOM: React uses a virtual DOM to efficiently update and render.
  • Provides modern framework features like JSX, fragments, Context, Portals, Suspense, streaming SSR, progressive hydration, Error Boundaries, and concurrent rendering.
  • Maintained by Facebook and the community.

Side by side comparison Solidjs vs React(functional component)

Components:

React:

React components can be created using class-based syntax or function-based syntax. Components are functions that return JSX.

// Function-based syntax
const Hello = () => <div>Hello</div>;
Enter fullscreen mode Exit fullscreen mode
Solidjs:

Components are functions that return JSX.

const Hello = () => <div>Hello</div>;
Enter fullscreen mode Exit fullscreen mode

Note: Solidjs and React both use the same JSX for templates.


State: State is a plain JavaScript object that is used to record and react to user interactions.

React:

A state is a plain object. You can create a state using the useState hook. useState takes the default state as a parameter and returns an array of state and state setter functions.

const Counter = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  // OR
  const increment = () => setCount((c) => c + 1);
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>Click</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:

You can create state(signal) using createSignal hook. createSignal takes default state(signal) as an parameter and returns array of state(signal) and state(signal) setter function.

const Counter = () => {
  const [count, setCount] = createSignal(0);
  const increment = () => setCount(count() + 1);
  // OR
  const increment = () => setCount((c) => c + 1);
  return (
    <div>
      <h1>{count()}</h1>
      <button onClick={increment}>Click</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

NOTE: React Hooks can only be called inside the root of the component. Solid createSignal can be used outside of a component.

const [count, setCount] = useState(0); // Not allowed
useEffect(() => {}, []); // Not allowed
const Counter = () => {};

const [count, setCount] = createSignal(0); // Allowed
createEffect(() => {}); // Allowed
const Counter = () => {};
Enter fullscreen mode Exit fullscreen mode

Effects(side effect): It's a function that runs when state changes.

React:

In React we have to pass the dependencies array to the useEffect hook.

There are 3 ways to do it:

  1. Without dependencies array (the effect will be called on every render)
  2. With dependencies array (the effect will be called only when dependencies change)
  3. With empty dependencies array (the effect will be called only once)
const Counter = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log('I am called on every render');
  });

  useEffect(() => {
    console.log('I am called only when count changes');
  }, [count]);

  useEffect(() => {
    console.log('I am called only once');
  }, []);
  return ...
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:

In Solidjs we don't have to pass dependencies array like the useEffect hook. It'll automatically detect dependencies and call effect only when dependencies change.

const Counter = () => {
  const [count, setCount] = createSignal(0);
  createEffect(() => {
    console.log('I am called only once');
  });

  createEffect(() => {
    console.log('I am called only when count changes',count());
  });
  return ...
};
Enter fullscreen mode Exit fullscreen mode

Lifecycle: It helps to monitor and manipulate the state.

React:
const Counter = () => {
  useEffect(() => {
    console.log('I am called onMount');

    return () => console.log('I am called onUnmount');
  }, []);
  return ...
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:
const Counter = () => {
  onMount(() => console.log('I am called onMount'));
  onCleanup(() =>  console.log('I am called onUnmount'));
  return ...
};
Enter fullscreen mode Exit fullscreen mode

Refs: It's a way to access DOM elements.

React:
const Counter = () => {
  const ref = useRef();
  useEffect(() => ref.current.focus(), [ref]);
  return <input ref={ref} />;
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:
const Counter = () => {
  let ref;
  onMount(() => ref?.focus());
  return <input ref={ref} />;
};
Enter fullscreen mode Exit fullscreen mode

Props: It's a way to pass data to components. It's a plain JavaScript object.

React:

Props are passed as an object and can be destructured.

const Counter = (props) => {
  return <div>{props.count}</div>; // Valid
};

const Counter = ({ count }) => {
  return <div>{count}</div>; // Valid
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:

Props are passed as an object and can't be destructured.

const Counter = (props) => {
  return <div>{props.count()}</div>; // Valid
};

const Counter = ({ count }) => {
  return <div>{count()}</div>; // Not Valid
};
Enter fullscreen mode Exit fullscreen mode

List of Components/Elements:

React:

For multiple lists of data to be rendered, we can use the map function.

const Counter = () => {
  const list = [1, 2, 3];
  return (
    <div>
      {list.map((item) => (
        <div>{item}</div>
      ))}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:

For multiple lists of data to be rendered, we can use the map function or For component.

const Counter = () => {
  const list = [1, 2, 3];
  return (
    <>
      {list.map((item) => (
        <div>{item}</div>
      ))}

      <For each={list} fallback={<div>Loading...</div>}>
        {(item) => <div>{item}</div>}
      </For>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Conditional Rendering: It's a way to render a component based on condition.

React:
const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <h1>{count < 5 ? "True Value" : "Falsy Value"}</h1>
      <button onClick={() => setCount(count + 1)}>Click</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
Solidjs:
const Counter = () => {
  const count = 5;
  return (
    <div>
      <h1>{count < 5 ? "True Value" : "Falsy Value"}</h1>
      // OR
      <Show when={count < 5} fallback={<h1>Falsy Value</h1>}>
        <h1>True Value</h1>
      </Show>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Note: Solidjs doesn't rerender the component. It'll always render the first evaluated value.

const Counter = () => {
  const [count, setCount] = createSignal(0);
  const TrueCase = (
    <div>
      <h1>From True Value </h1>
      <button onClick={() => setCount((c) => c + 1)}>Click {count()}</button>
    </div>
  );

  const FalseCase = (
    <div>
      <h1>From False Value</h1>
      <button onClick={() => setCount((c) => c + 1)}>Click {count()}</button>
    </div>
  );

  if (count() < 5) return TrueCase;
  return FalseCase; // Never render this
};

// Solution:
const Counter = () => {
  const [count, setCount] = createSignal(0);
  const TrueCase = (
    <div>
      <h1>From True Value </h1>
      <button onClick={() => setCount((c) => c + 1)}>Click {count()}</button>
    </div>
  );

  const FalseCase = (
    <div>
      <h1>From False Value</h1>
      <button onClick={() => setCount((c) => c + 1)}>Click {count()}</button>
    </div>
  );

  return (
    <Show when={count() < 5} fallback={FalseCase}>
      {TrueCase}
    </Show>
  );
};
Enter fullscreen mode Exit fullscreen mode

Context: It's a way to share data between sibling/child components.

React:
const CountContext = React.createContext(0);
const Provider = ({ children }) => {
  const [count, setCount] = useState(0);
  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
};

const Counter = () => {
  const { count, setCount } = useContext(CountContext);
  return <h1 onClick={() => setCount((c) => c + 1)}>{count}</h1>;
};

const App = () => {
  return (
    <Provider>
      <Counter />
      <Counter />
    </Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Note: use can use context with useReducer, Instead of directly calling setCount.

Solidjs:
export const CounterContext = createContext([{ count: 0 }, {}]);
export function CounterProvider(props) {
  const [state, setState] = createStore({ count: props.count || 0 });
  const store = [
    state,
    {
      increment: () => setState("count", (c) => c + 1),
    },
  ];

  return (
    <CounterContext.Provider value={store}>
      {props.children}
    </CounterContext.Provider>
  );
}

const Counter = () => {
  const [state, { increment }] = useContext(CounterContext);
  return <h1 onClick={increment}>{state.count}</h1>;
};

const App = () => (
  <CounterProvider>
    <Counter />
    <Counter />
  </CounterProvider>
);
Enter fullscreen mode Exit fullscreen mode

Solid offers many more features like a store for state management check the API doc for more info.

Live Demo: Counter Demo


Thank you for reading 😊

Got any questions or additional? please leave a comment.


Must Read If you haven't
useAsync hook with cache
Getting started with SolidJs – A Beginner's Guide
React best practices and patterns to reduce code
3 steps to create custom state management library with React and Context API
How to cancel Javascript API request with AbortController
13 Typescript Utility: A Cheat Sheet for Developer

More content at Dev.to.
Catch me on YouTube, Github, Twitter, LinkedIn, Medium, Stackblitz, Hashnode, HackerNoon, and Blogspot.

Top comments (3)

Collapse
 
lexlohr profile image
Alex Lohr

Tiny little issue with your second solid example description: you used props.count as an accessor instead of a value, which would allow destructuring from props, since the getter is immutable and will not lose its reactivity. However, it is encouraged to use the value instead and avoid destructuring completely in order to avoid errors.

Also, you only showed the parts of solid with a direct equivalent in react, but even though it is much smaller, it comes with even more versatile state management tools: stores (with produce and reconcile, which allows immer-like states) and mutables (to have state objects like in Vue).

Lastly, react provides it's users with some really good debugging tools (which unfortunately are necessary, because the constant re-running of components render normal developer tools almost unusable), while solid can be debugged using normal dev tools; the compiled sources are surprisingly readable.

Apart from that, it's a really well written comparison. Thank you!

Collapse
 
devsmitra profile image
Rahul Sharma

Thanks for pointing out the issue and feedback.

Collapse
 
pepsiamir profile image
pepsiamir

This article is Darn Good. I like simplicity of comparison. I learned a lot