SolidJS has been turning heads in the web development world, and for good reason. As someone who’s been knee-deep in JavaScript frameworks for years, I was skeptical at first. Another framework? Really? But SolidJS isn’t just another drop in the ocean – it’s making waves with its fresh take on reactivity and performance.
Let’s start with the basics. SolidJS is a declarative JavaScript library for creating user interfaces. It shares some similarities with React, which might make it feel familiar if you’re coming from that ecosystem. But where SolidJS really shines is in its approach to reactivity and how it handles updates.
One of the things that caught my attention was SolidJS’s fine-grained reactivity system. Instead of using a virtual DOM (VDOM) like React does, SolidJS compiles your components into real DOM operations. This means updates are incredibly precise – only the exact parts of the DOM that need to change are touched.
Here’s a simple example to illustrate this:
import { createSignal, onCleanup } from 'solid-js';
import { render } from 'solid-js/web';
const App = () => {
const [count, setCount] = createSignal(0);
const timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
return <div>Count: {count()}</div>;
};
render(() => <App />, document.getElementById('app'));
In this example, we’re creating a simple counter that updates every second. The createSignal
function is used to create a reactive value, and the onCleanup
function ensures we clean up our interval when the component is destroyed.
What’s happening under the hood is fascinating. SolidJS is not creating a bunch of objects to represent your DOM tree. Instead, it’s directly manipulating the DOM when needed. This leads to blazing fast performance and minimal overhead.
But SolidJS isn’t just about speed – it’s about rethinking how we approach reactivity in web applications. The framework introduces the concept of signals and derivations. Signals are like reactive variables, while derivations are computations that depend on these signals.
Let’s expand our example to see how this works:
import { createSignal, createMemo } from 'solid-js';
function Counter() {
const [count, setCount] = createSignal(0);
const doubleCount = createMemo(() => count() * 2);
return (
<>
<p>Count: {count()}</p>
<p>Double Count: {doubleCount()}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</>
);
}
In this example, count
is a signal, and doubleCount
is a derivation. Whenever count
changes, doubleCount
will automatically update. This creates a clear and predictable flow of data in your application.
One thing I love about SolidJS is how it encourages you to think about your application’s state and how it changes over time. It’s not just about updating the UI – it’s about modeling your application’s behavior in a way that’s both intuitive and efficient.
Another aspect of SolidJS that’s worth exploring is its approach to components. Unlike React, SolidJS components are not classes or functions that return JSX. Instead, they’re factories that create DOM nodes. This might sound a bit strange at first, but it leads to some interesting possibilities.
Here’s an example of a more complex component in SolidJS:
import { createSignal, For } from 'solid-js';
const TodoList = () => {
const [todos, setTodos] = createSignal([]);
const [newTodo, setNewTodo] = createSignal('');
const addTodo = (e) => {
e.preventDefault();
setTodos([...todos(), { id: Date.now(), text: newTodo(), completed: false }]);
setNewTodo('');
};
const toggleTodo = (id) => {
setTodos(todos().map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<>
<form onSubmit={addTodo}>
<input
type="text"
value={newTodo()}
onInput={(e) => setNewTodo(e.target.value)}
/>
<button type="submit">Add Todo</button>
</form>
<ul>
<For each={todos()}>
{(todo) => (
<li
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</li>
)}
</For>
</ul>
</>
);
};
This TodoList component demonstrates several key features of SolidJS. We’re using signals to manage state (todos
and newTodo
), and we’re using the For
component to efficiently render our list of todos. Notice how we’re able to directly modify our todos array using the setTodos
function – SolidJS will automatically update only the parts of the DOM that need to change.
One of the things that really stands out to me about SolidJS is its simplicity. Despite its powerful features, the API is surprisingly small and easy to learn. This is a breath of fresh air in a world where many frameworks seem to be growing more complex with each release.
But don’t let this simplicity fool you – SolidJS is capable of handling complex applications with ease. Its reactive system scales well, and its performance remains impressive even as your application grows.
Speaking of performance, let’s dive a bit deeper into why SolidJS is so fast. The key lies in its compilation step. When you write SolidJS code, it’s compiled into highly optimized JavaScript that updates the DOM directly. There’s no intermediate representation, no diffing algorithm running on every update. This means that SolidJS can achieve performance that’s often better than even hand-optimized code.
Here’s a simple benchmark to illustrate this:
import { createSignal, onCleanup } from 'solid-js';
function Benchmark() {
const [count, setCount] = createSignal(0);
const timer = setInterval(() => {
setCount(c => c + 1);
if (count() >= 1000000) clearInterval(timer);
}, 0);
onCleanup(() => clearInterval(timer));
return <div>Count: {count()}</div>;
}
This benchmark will update the count as fast as possible until it reaches a million. On my machine, this completes in just a few seconds – a testament to the efficiency of SolidJS’s reactivity system.
But SolidJS isn’t just about raw performance. It also provides a great developer experience. The error messages are clear and helpful, the documentation is comprehensive, and the community, while still growing, is friendly and supportive.
One aspect of SolidJS that I particularly appreciate is its approach to side effects. In many frameworks, managing side effects can be tricky, leading to bugs and hard-to-trace issues. SolidJS provides a simple and intuitive way to handle side effects with its createEffect
function.
Here’s an example:
import { createSignal, createEffect } from 'solid-js';
function UserProfile() {
const [userId, setUserId] = createSignal(1);
const [user, setUser] = createSignal(null);
createEffect(async () => {
const response = await fetch(`https://api.example.com/users/${userId()}`);
const data = await response.json();
setUser(data);
});
return (
<>
<input
type="number"
value={userId()}
onInput={(e) => setUserId(e.target.value)}
/>
{user() ? (
<div>
<h2>{user().name}</h2>
<p>{user().email}</p>
</div>
) : (
<p>Loading...</p>
)}
</>
);
}
In this example, createEffect
is used to fetch user data whenever the userId
changes. This effect will run after the initial render and then again whenever any of its dependencies (in this case, userId
) change. This makes it easy to keep your UI in sync with your data without having to manually trigger updates.
Another powerful feature of SolidJS is its context system. If you’ve used React’s context API, you’ll find SolidJS’s approach familiar, but with some added benefits. Here’s a quick example:
import { createContext, useContext, createSignal } from 'solid-js';
const ThemeContext = createContext();
function ThemeProvider(props) {
const [theme, setTheme] = createSignal('light');
const value = [theme, setTheme];
return (
<ThemeContext.Provider value={value}>
{props.children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const [theme, setTheme] = useContext(ThemeContext);
return (
<button
style={{ background: theme() === 'light' ? '#fff' : '#000', color: theme() === 'light' ? '#000' : '#fff' }}
onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
This example demonstrates how easy it is to create and use context in SolidJS. The context value can be any type of data, including signals and stores, making it incredibly flexible.
As I’ve been working with SolidJS, I’ve found myself constantly impressed by how well thought-out the API is. Everything feels cohesive and purposeful. There’s a clear philosophy behind the framework – one that prioritizes performance, simplicity, and developer experience.
But like any technology, SolidJS isn’t without its challenges. The ecosystem, while growing, is still smaller than that of more established frameworks like React or Vue. This means you might sometimes find yourself reaching for lower-level APIs or writing your own solutions where in other frameworks you might have used a third-party library.
However, I see this as an opportunity rather than a drawback. It’s a chance to really understand the problems you’re solving and to create solutions that are tailored to your specific needs. And as the SolidJS community grows, I’m excited to see the innovative libraries and patterns that will emerge.
One area where SolidJS really shines is in its handling of asynchronous operations. The createResource
function makes it easy to work with asynchronous data in a reactive way. Here’s an example:
import { createResource, createSignal } from 'solid-js';
const fetchUser = async (id) => {
const response = await fetch(`https://api.example.com/users/${id}`);
return response.json();
};
function UserProfile() {
const [userId, setUserId] = createSignal(1);
const [user] = createResource(userId, fetchUser);
return (
<>
<input
type="number"
value={userId()}
onInput={(e) => setUserId(e.target.value)}
/>
<Show when={!user.loading} fallback={<p>Loading...</p>}>
<div>
<h2>{user().name}</h2>
<p>{user().email}</p>
</div>
</Show>
</>
);
}
In this example, createResource
is used to fetch user data based on the userId
. The user
resource automatically tracks its loading and error states, making it easy to handle different scenarios in your UI.
As I’ve delved deeper into SolidJS, I’ve found myself rethinking many of the patterns and practices I’ve used in other frameworks. It’s not just about translating React or Vue code into SolidJS – it’s about embracing a new way of thinking about reactivity and state management.
For instance, SolidJS’s stores provide a powerful way to manage complex state. Unlike React’s reducers or Vue’s Vuex, SolidJS stores are deeply reactive. Here’s a simple example:
import { createStore } from 'solid-js/store';
const [state, setState] = createStore({
user: { name: 'John', age: 30 },
posts: [
{ id: 1, title: 'Hello World' },
{ id: 2, title: 'SolidJS is awesome' }
]
});
// Update a nested property
setState('user', 'age', 31);
// Add a new post
setState('posts', posts => [...posts, { id: 3, title: 'New Post' }]);
// Update a specific post
setState('posts', p => p.id === 2, 'title', 'SolidJS is really awesome');
This approach to state management feels incredibly natural and intuitive. It’s easy to update nested properties or specific items in an array without having to worry about immutability or writing complex reducer functions.
As I wrap up this deep dive into SolidJS, I can’t help but feel excited about the future of web development. SolidJS represents a step forward in how we think about building reactive user interfaces. Its focus on performance, simplicity, and developer experience makes it a joy to work with.
Whether you’re building a small personal project or a large-scale application, SolidJS provides the tools you need to create fast, efficient, and maintainable web applications. It challenges us to rethink our approaches to state management, component composition, and reactivity.
As with any new technology, the best way to understand SolidJS is to start building with it. Try recreating some of your favorite React or Vue components in SolidJS. Experiment with its reactive primitives. Push the boundaries of what you think is possible in a web application.
The web development landscape is constantly evolving, and frameworks like SolidJS are at the forefront of this evolution. By embracing new ideas and approaches, we can create better, faster, and more user-friendly web applications. And isn’t that what it’s all about?
So, are you ready to give SolidJS a try? Dive in, start building, and see for yourself why so many developers are excited about this innovative framework. Who knows? You might just find your new favorite tool for building web applications.