Stopping Unnecessary Rerenders: Mastering Signals and Memos in SolidJS
Learn how SolidJS uses Signals and Memos to achieve fine-grained reactivity, avoiding the Virtual DOM and preventing unnecessary component rerenders.
22 Sept 2026, 04:35 UTC

The Cost of Component-Level Rerendering
In many modern UI frameworks, a state change triggers a re-execution of the entire component function. This often leads to a "Virtual DOM diffing" process where the framework compares a new tree of elements against an old one to decide what to update. As applications grow, these diffing cycles can become a performance bottleneck, especially in complex lists or high-frequency data dashboards.
SolidJS solves this by abandoning the Virtual DOM entirely. Instead, it uses Fine-Grained Reactivity. In Solid, your component function runs exactly once. The reactivity is not tied to the component, but to the specific DOM nodes that depend on the data. The takeaway is simple: to keep your app fast, you must move your logic out of the component's top-level scope and into reactive primitives.
The Core Primitives: Signals and Effects
At the heart of this system are Signals. A Signal is a getter and a setter pair. When you call the getter inside a JSX expression or an effect, Solid records that specific location as a subscriber.
createSignal: The basic unit of state.createEffect: A function that runs whenever its dependencies (Signals) change. This is where you handle side effects like API calls or manual DOM manipulations.
Because the component function only runs once, any code written directly in the function body is essentially a "setup" phase. If you place a console.log at the top of your component, you will see it fire once when the component mounts, and never again—even if the state changes.
Optimizing Derived State with Memos
While Signals handle raw data, Memos (via createMemo) handle derived data. A Memo is a read-only Signal that caches its value. It only re-calculates when its dependencies change, and it only notifies its own subscribers if the resulting value actually differs from the previous one.
Without Memos, a complex calculation inside a JSX expression would re-run every time any Signal it depends on changes. By wrapping that calculation in a Memo, you create a "circuit breaker" that prevents unnecessary downstream updates.
Practical Example: A Filtered Search List
Consider a scenario where you have a large list of items and a search query. You want to filter the list without re-rendering the entire list container.
import { createSignal, createMemo } from "solid-js";
function SearchComponent() {
const [query, setQuery] = createSignal("");
const [items] = createSignal(["Apple", "Banana", "Cherry", "Date", "Elderberry"]);
// This is a Memo. It only recalculates when 'query' or 'items' changes.
const filteredItems = createMemo(() => {
console.log("Filtering list...");
return items().filter(item =>
item.toLowerCase().includes(query().toLowerCase())
);
});
return (
<div>
<input
type="text"
onInput={(e) => setQuery(e.currentTarget.value)}
placeholder="Search fruits..."
/>
{/* The map function here is wrapped in a reactive scope */}
{filteredItems().map(item => (<li>{item}</li>))}
</div>
);
}
Execution Note: Run this in a standard SolidJS environment. You will notice that the "Filtering list..." log only appears when the input changes, and only the <li> elements are added or removed from the DOM. The rest of the SearchComponent remains untouched.
The "Reactivity Killer": Destructuring Props
One of the most common pitfalls in SolidJS is destructuring props. In frameworks like React, destructuring is standard. In Solid, it is a bug.
When you write const { name } = props;, you are accessing the value of the prop at the moment the component function runs (which is only once). You have effectively stripped away the reactive proxy. To maintain reactivity, you must always access props as props.name within your JSX or Memos.
Trade-offs and Limitations
Fine-grained reactivity is not a free lunch. Every Signal and Memo creates a subscription in memory. If you create thousands of Memos for trivial calculations (e.g., adding two numbers), the memory overhead of managing the dependency graph can exceed the CPU cost of simply recalculating the value. Use Memos for expensive operations or to prevent expensive DOM updates, not for every single variable.
Verifying the Result
To verify that your optimizations are working, use the browser's Elements panel in DevTools. When a state change occurs, observe the DOM. In a Virtual DOM framework, you might see a larger chunk of the tree flash or update. In SolidJS, only the specific text node or attribute tied to the Signal should change. You can also use the SolidJS DevTools to inspect the dependency graph and ensure your Memos are not triggering more often than necessary.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.