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 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 of Solid.js 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.

const Hello = () => <div>Hello</div>;

Solidjs:

Components are functions that return JSX.

const Hello = () => <div>Hello</div>;

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>
);
};

Solidjs:

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

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>
);
};

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 = () => {};

Effects(side effect): It’s a function that runs when the 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 ...
};

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 ...
};

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 ...
};

Solidjs:

const Counter = () => {
onMount(() => console.log('I am called onMount'));
onCleanup(() => console.log('I am called onUnmount'));
return ...
};

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

React:

const Counter = () => {
const ref = useRef();
useEffect(() => ref.current.focus(), [ref]);
return <input ref={ref} />;
};

Solidjs:

const Counter = () => {
let ref;
onMount(() => ref?.focus());
return <input ref={ref} />;
};

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
};

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
};

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>
);
};

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>
</>
);
};

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>
);
};

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>
);
};

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>
);
};

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>
);
};

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>
);

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

React.js state management using signals

useAsync hook with cache (devsmitra.blogspot.com)

More content at Blogspot.

Catch me on GithubTwitterLinkedInMediumDev.to, and Stackblitz.

Next Post Previous Post
No Comment
Add Comment
comment url