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>;
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 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>
);
};
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 state changes.
React:
In React we have to pass the dependencies array to the useEffect hook.
There are 3 ways to do it:
- Without dependencies array (the effect will be called on every render)
- With dependencies array (the effect will be called only when dependencies change)
- 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.
Top comments (3)
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!
Thanks for pointing out the issue and feedback.
This article is Darn Good. I like simplicity of comparison. I learned a lot