Solid.js has been getting a lot of attention among frontend developers lately. It uses React-like syntax while delivering performance that’s only 5% slower than vanilla JavaScript. In this guide, we’ll explore what Solid.js is, how to use it, and why so many developers are taking notice.

1. What is Solid.js?
Solid.js is a JavaScript library for building web interfaces. Created by Ryan Carniato, it started in the early 2020s and has been growing steadily ever since.
The biggest difference from React or Vue is that it doesn’t use a Virtual DOM. Instead, it compiles code directly to real DOM and updates only the parts that need changing. Think of it like precision surgery – targeting exactly what needs to be fixed rather than redoing everything. No wonder it’s so much faster!
The latest version is 1.9.9, available via npm. Version 2.0 is also in development, making this framework even more exciting for the future.
Three Core Principles of Solid.js
Solid.js feels really comfortable to work with, and these three principles explain why:
- Components run only once: They execute once when first rendered, then only the changed parts update. Unlike React, the entire component doesn’t keep re-running.
- Automatic dependency tracking: When you use data, it automatically “subscribes” to that data and updates only when it changes. No need to manually manage dependencies.
- Read/write separation: Reading and modifying data are clearly separated, making code intent immediately clear – “Oh, this reads data” or “This modifies it.”
These principles keep code intuitive and reduce bugs.
2. Why It’s Fast Without Virtual DOM: The Secret of Fine-Grained Reactivity
“Wasn’t Virtual DOM supposed to be fast?” you might wonder. It was, for a while. When DOM wasn’t optimized for large-scale updates, Virtual DOM was a faster alternative.
But Solid.js chose a different approach. When you create state and use it in your app, only the code that depends on that state re-runs when it changes. This is called “Fine-Grained Reactivity.”
Let’s Break It Down
Imagine adding a product to a shopping cart:
How React works:
- Re-renders the entire cart component
- If there are 10 products and you add 1, all 11 get re-rendered
- Compares Virtual DOM, then applies changes to real DOM
How Solid.js works:
- Directly adds just the new product to the DOM
- Doesn’t touch the other 10 products
- No comparison overhead
This difference significantly impacts performance. Benchmark tests show Solid.js is 2-3x faster than React.
The Compiler’s Magic
Solid.js analyzes templates at build time and converts them to optimized JavaScript. Here’s what happens:
// What we write
function Counter() {
const [count, setCount] = createSignal(0);
return <button onClick={() => setCount(c => c + 1)}>{count()}</button>;
}
// What the compiler generates (simplified)
function Counter() {
const [count, setCount] = createSignal(0);
const button = document.createElement('button');
button.onclick = () => setCount(c => c + 1);
// Only this part re-runs when count changes
createEffect(() => {
button.textContent = count();
});
return button;
}
It creates actual DOM nodes directly and reactively connects only the parts that need updating. That’s the core of Solid.js’s speed.
What is ‘React’? The Most Popular Component-Based JavaScript Library
3. Easy Learning Curve for React Developers
Another appeal of Solid.js is its familiarity. It follows similar philosophies to React: unidirectional data flow, read/write segregation, and immutable interfaces.
But there’s one important difference. In Solid, components run only once when first rendered, and hooks and bindings only execute when their dependencies update. No worrying about re-renders like in React.
Comparing React and Solid.js Code
React way:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const doubleCount = count * 2;
console.log('Runs on every render!');
return (
<button onClick={() => setCount(count + 1)}>
Count: {doubleCount}
</button>
);
}
Solid.js way:
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
const doubleCount = () => count() * 2;
console.log("This function runs only once!");
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {doubleCount()}
</button>
);
}
Key differences:
- Signals must be called as functions:
count()⭕ /count❌ - Components run once, so performance optimization is automatic
- No need for
useMemo,useCallbackoptimization hooks
4. Is It Really That Fast? Real Numbers
Let me show you with actual numbers. According to the JS Framework Benchmark, Solid.js performs only about 5% slower than vanilla JavaScript. That’s impressive for a framework. React, even when optimized, is nearly 100% slower than vanilla JS.
Detailed Metrics
Rendering Speed (creating 1,000 rows):
- Solid.js: Average 43.5ms
- React Hooks: Average 45.6ms
- React-Redux: Average 49.1ms
Milliseconds might seem small, but they add up and users definitely notice.
Bundle Size (production build, gzipped):
- Solid.js: 11.1kb (framework 6.2kb + app code 4.9kb)
- React 19: 47.3kb (React runtime 32.1kb + app code 15.2kb)
Solid.js is over 4x smaller!
Memory Usage:
- Solid.js uses about 26% more than vanilla JavaScript
- React uses 80-120% more than vanilla JavaScript
What Does This Mean in Practice?
Consider a user on a mobile 3G network:
- Solid.js app: Loads in about 0.5 seconds
- React app: Takes 2+ seconds
That 1.5-second difference seems small but makes the distinction between a “fast site” and a “slow site” from a user’s perspective.
5. Getting Started: From Installation to First Project
Ready to try Solid.js? If you have Node.js (v18+ recommended) installed, you can start right away.
Setting Up Your Environment
What you’ll need:
- Node.js 18 or higher (https://nodejs.org/)
- Code editor (VS Code recommended)
- VS Code extension: “TypeScript and JavaScript Language Features”
Creating a Project
Open your terminal and enter:
# Using npm
npm create solid@latest my-solid-app
# Or using pnpm (faster)
pnpm create solid my-solid-app
You’ll get some interactive prompts:
? Which template would you like to use?
> bare (basic template)
with-tailwindcss (includes Tailwind CSS)
with-typescript (includes TypeScript)
? Use Server-Side Rendering?
> Yes (important for SEO)
No (simple SPA)
? Use TypeScript?
> Yes (recommended - type safety)
No
Understanding the Project Structure
Here’s what gets generated:
my-solid-app/
├── public/ # Static files
│ ├── favicon.ico
│ └── assets/ # Images, fonts, etc.
├── src/
│ ├── routes/ # Pages (file-based routing)
│ │ └── index.tsx # Homepage (/)
│ ├── components/ # Reusable components
│ ├── entry-client.tsx # Client entry point
│ ├── entry-server.tsx # Server entry point
│ └── app.tsx # HTML root
├── package.json
├── tsconfig.json # TypeScript config
└── vite.config.ts # Vite build config
Key file explanations:
- src/routes/: Files here automatically become routes
index.tsx→/about.tsx→/aboutusers/[id].tsx→/users/:id(dynamic route)
- src/entry-client.tsx: Where your app starts in the browser
- src/entry-server.tsx: Used for server rendering
- app.tsx: Defines
<html>,<head>,<body>tags
Running the Development Server
Navigate to your project folder:
cd my-solid-app
npm install # Install dependencies
npm run dev # Start dev server
Open http://localhost:3000 in your browser. Hot Module Replacement (HMR) will instantly reflect any file changes.
6. Mastering Core Concepts
createSignal: Basic Reactive State
Signals are Solid.js’s most fundamental reactive primitive.
import { createSignal } from "solid-js";
function UserProfile() {
// Returns [getter, setter]
const [name, setName] = createSignal("John Doe");
const [age, setAge] = createSignal(25);
return (
<div>
<p>Name: {name()}</p>
<p>Age: {age()}</p>
<button onClick={() => setAge(age() + 1)}>
Happy Birthday!
</button>
<input
type="text"
value={name()}
onInput={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
</div>
);
}
Important notes:
- Always call signals as functions to read:
name()✅,name❌ - Same in JSX:
{name()}✅,{name}❌
createEffect: Managing Side Effects
Automatically runs code when signal values change:
import { createSignal, createEffect } from "solid-js";
function SearchComponent() {
const [searchTerm, setSearchTerm] = createSignal("");
const [results, setResults] = createSignal([]);
// Automatically runs when searchTerm changes
createEffect(() => {
const term = searchTerm();
if (term.length > 2) {
// API call
fetch(`https://api.example.com/search?q=${term}`)
.then(res => res.json())
.then(data => setResults(data));
} else {
setResults([]);
}
});
return (
<div>
<input
type="text"
value={searchTerm()}
onInput={(e) => setSearchTerm(e.target.value)}
placeholder="Search (3+ chars)"
/>
<ul>
<For each={results()}>
{(item) => <li>{item.title}</li>}
</For>
</ul>
</div>
);
}
createMemo: Derived State
Caches expensive computed values:
import { createSignal, createMemo } from "solid-js";
function ShoppingCart() {
const [items, setItems] = createSignal([
{ id: 1, name: "Laptop", price: 1000, quantity: 1 },
{ id: 2, name: "Mouse", price: 50, quantity: 2 },
]);
// Only recalculates when items change
const totalPrice = createMemo(() => {
console.log("Calculating total...");
return items().reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
});
const formattedTotal = createMemo(() =>
`$${totalPrice().toLocaleString()}`
);
return (
<div>
<h2>Shopping Cart</h2>
<For each={items()}>
{(item) => (
<div>
<span>{item.name}</span>
<span>${item.price} × {item.quantity}</span>
</div>
)}
</For>
<div class="total">
<strong>Total: {formattedTotal()}</strong>
</div>
</div>
);
}
createStore: Complex Object State
Use stores for nested objects or arrays:
import { createStore } from "solid-js/store";
function TodoApp() {
const [todos, setTodos] = createStore([
{ id: 1, text: "Learn Solid.js", completed: false },
{ id: 2, text: "Build an app", completed: false }
]);
// Update specific item (fine-grained reactivity)
const toggleTodo = (id) => {
setTodos(
(todo) => todo.id === id, // Condition
"completed", // Property
(completed) => !completed // Update function
);
};
const addTodo = (text) => {
setTodos([...todos, {
id: Date.now(),
text,
completed: false
}]);
};
return (
<div>
<For each={todos}>
{(todo) => (
<div
onClick={() => toggleTodo(todo.id)}
style={{
"text-decoration": todo.completed ? "line-through" : "none"
}}
>
{todo.text}
</div>
)}
</For>
</div>
);
}
Lifecycle Management
import { onMount, onCleanup, createSignal } from "solid-js";
function Timer() {
const [seconds, setSeconds] = createSignal(0);
// Runs after component mounts to DOM
onMount(() => {
console.log("Timer component mounted");
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup when component is removed
onCleanup(() => {
clearInterval(interval);
console.log("Timer component removed");
});
});
return <div>Elapsed time: {seconds()} seconds</div>;
}
7. Learning Through Examples
Let’s look at practical patterns with code you can copy and use right away.
Example 1: API Calls with Loading States
import { createSignal, createResource, Show, For } from "solid-js";
// Fetch async data with createResource
function UserList() {
const fetchUsers = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
return response.json();
};
// Returns [data, { refetch, mutate }]
const [users, { refetch }] = createResource(fetchUsers);
return (
<div>
<h2>User List</h2>
<button onClick={refetch}>Refresh</button>
{/* Handle loading state */}
<Show
when={!users.loading}
fallback={<div>Loading...</div>}
>
<Show
when={!users.error}
fallback={<div>Error: {users.error.message}</div>}
>
<ul>
<For each={users()}>
{(user) => (
<li>
<strong>{user.name}</strong> - {user.email}
</li>
)}
</For>
</ul>
</Show>
</Show>
</div>
);
}
Example 2: Form Validation
import { createSignal, createMemo } from "solid-js";
function SignupForm() {
const [email, setEmail] = createSignal("");
const [password, setPassword] = createSignal("");
const [confirmPassword, setConfirmPassword] = createSignal("");
// Validation logic
const emailError = createMemo(() => {
const value = email();
if (!value) return "";
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
? ""
: "Invalid email format";
});
const passwordError = createMemo(() => {
const value = password();
if (!value) return "";
return value.length >= 8
? ""
: "Password must be at least 8 characters";
});
const confirmPasswordError = createMemo(() => {
if (!confirmPassword()) return "";
return password() === confirmPassword()
? ""
: "Passwords don't match";
});
const isValid = createMemo(() =>
email() &&
password() &&
!emailError() &&
!passwordError() &&
!confirmPasswordError()
);
const handleSubmit = (e) => {
e.preventDefault();
if (isValid()) {
console.log("Sign up:", { email: email(), password: password() });
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Email</label>
<input
type="email"
value={email()}
onInput={(e) => setEmail(e.target.value)}
placeholder="example@email.com"
/>
{emailError() && <span class="error">{emailError()}</span>}
</div>
<div>
<label>Password</label>
<input
type="password"
value={password()}
onInput={(e) => setPassword(e.target.value)}
placeholder="8+ characters"
/>
{passwordError() && <span class="error">{passwordError()}</span>}
</div>
<div>
<label>Confirm Password</label>
<input
type="password"
value={confirmPassword()}
onInput={(e) => setConfirmPassword(e.target.value)}
/>
{confirmPasswordError() &&
<span class="error">{confirmPasswordError()}</span>
}
</div>
<button type="submit" disabled={!isValid()}>
Sign Up
</button>
</form>
);
}
Example 3: Pagination
import { createSignal, createMemo, For } from "solid-js";
function PaginatedList() {
// All data (normally fetched from API)
const allItems = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
title: `Item ${i + 1}`
}));
const [currentPage, setCurrentPage] = createSignal(1);
const itemsPerPage = 10;
// Current page items
const currentItems = createMemo(() => {
const start = (currentPage() - 1) * itemsPerPage;
const end = start + itemsPerPage;
return allItems.slice(start, end);
});
// Total pages
const totalPages = createMemo(() =>
Math.ceil(allItems.length / itemsPerPage)
);
// Page number array
const pageNumbers = createMemo(() =>
Array.from({ length: totalPages() }, (_, i) => i + 1)
);
return (
<div>
<h2>Pagination Example</h2>
<ul>
<For each={currentItems()}>
{(item) => <li>{item.title}</li>}
</For>
</ul>
<div class="pagination">
<button
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage() === 1}
>
Previous
</button>
<For each={pageNumbers()}>
{(pageNum) => (
<button
onClick={() => setCurrentPage(pageNum)}
class={currentPage() === pageNum ? "active" : ""}
>
{pageNum}
</button>
)}
</For>
<button
onClick={() => setCurrentPage(p => Math.min(totalPages(), p + 1))}
disabled={currentPage() === totalPages()}
>
Next
</button>
</div>
<p>
Showing {allItems.length} items - Page {currentPage()} / {totalPages()}
</p>
</div>
);
}
Example 4: Global State with Context API
import { createContext, useContext, createSignal } from "solid-js";
// 1. Create context
const ThemeContext = createContext();
// 2. Create provider component
function ThemeProvider(props) {
const [theme, setTheme] = createSignal("light");
const toggleTheme = () => {
setTheme(t => t === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{props.children}
</ThemeContext.Provider>
);
}
// 3. Create custom hook
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within ThemeProvider");
}
return context;
}
// 4. Usage example
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header class={theme()}>
<h1>My Website</h1>
<button onClick={toggleTheme}>
{theme() === "light" ? "🌙 Dark Mode" : "☀️ Light Mode"}
</button>
</header>
);
}
function App() {
return (
<ThemeProvider>
<Header />
{/* Other components */}
</ThemeProvider>
);
}
8. SolidStart: Full-Stack Framework for Solid.js
If React has Next.js, Solid.js has SolidStart. Released officially in November 2024, it’s production-ready and stable.
Key Features
| Feature | Description | Example |
|---|---|---|
| File-based Routing | Folder structure becomes URL structure | routes/about.tsx → /about |
| API Routes | Automatic serverless function creation | routes/api/users.ts → /api/users |
| SSR/SSG | Choose server/static rendering | SEO optimization |
| Single-Flight Mutations | Automatic duplicate request prevention | Network efficiency |
| Islands Architecture | Partial hydration support | Performance optimization |
| Multi-platform Deploy | Netlify, Vercel, Cloudflare, etc. | Easy deployment |
Creating a SolidStart Project
# Create SolidStart project
npm create solid@latest my-app
# Options
# - Template: Choose SolidStart
# - SSR: Yes (server rendering)
# - TypeScript: Yes (recommended)
cd my-app
npm install
npm run dev
File-Based Routing
src/routes/
├── index.tsx → /
├── about.tsx → /about
├── blog/
│ ├── index.tsx → /blog
│ ├── [slug].tsx → /blog/:slug
│ └── new.tsx → /blog/new
└── users/
└── [id].tsx → /users/:id
Dynamic route example:
// src/routes/blog/[slug].tsx
import { useParams } from "@solidjs/router";
import { createResource, Show } from "solid-js";
export default function BlogPost() {
const params = useParams();
// Fetch blog post by slug
const [post] = createResource(() => params.slug, async (slug) => {
const res = await fetch(`https://api.example.com/posts/${slug}`);
return res.json();
});
return (
<Show when={!post.loading} fallback={<p>Loading...</p>}>
<article>
<h1>{post().title}</h1>
<div innerHTML={post().content} />
</article>
</Show>
);
}
Creating API Routes
// src/routes/api/users.ts
import { json } from "@solidjs/start";
// GET /api/users
export async function GET() {
const users = [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Jane Smith" }
];
return json(users);
}
// POST /api/users
export async function POST(event) {
const body = await event.request.json();
// Database save logic
const newUser = {
id: Date.now(),
name: body.name
};
return json(newUser, { status: 201 });
}
Using Server Actions
import { action, useAction } from "@solidjs/router";
import { createSignal } from "solid-js";
// Server-only function
const addTodo = action(async (formData: FormData) => {
"use server"; // Marks as server-only
const text = formData.get("text") as string;
// Save to database
await db.todos.create({ text, completed: false });
return { success: true };
});
function TodoForm() {
const submit = useAction(addTodo);
return (
<form action={submit} method="post">
<input type="text" name="text" required />
<button type="submit">Add</button>
</form>
);
}
9. Comparing with React
Let’s compare both frameworks at a glance:
| Aspect | React | Solid.js | Which is Better? |
|---|---|---|---|
| Rendering Performance | Good | Excellent (2-3x faster) | Solid.js |
| Bundle Size | 47.3kb | 11.1kb | Solid.js |
| Memory Usage | 80-120% increase | 26% increase | Solid.js |
| Learning Curve | Moderate | Easy (if you know React) | Solid.js |
| Library Ecosystem | Massive | Growing | React |
| Hiring Developers | Easy | Still challenging | React |
| Community | Huge | Growing actively | React |
| TypeScript | Excellent | Excellent | Tie |
| SSR | Needs Next.js | Built into SolidStart | Solid.js |
| State Management | External libs needed | Built-in | Solid.js |
| Optimization | Manual | Automatic | Solid.js |
Choosing Based on Project Type
Solid.js is great for:
- ✅ Performance-critical services (dashboards, charts, data viz)
- ✅ Mobile-heavy traffic where loading speed matters
- ✅ Apps with frequent real-time updates (chat, notifications, stock prices)
- ✅ SEO-important blogs or marketing sites
- ✅ New projects starting from scratch
- ✅ Small teams (5 or fewer)
React is better for:
- ✅ Large enterprise projects (10+ developers)
- ✅ Need for specific libraries like Material-UI
- ✅ Hiring developers is critical (more React devs available)
- ✅ Existing React codebase
- ✅ Need React Native for mobile apps
- ✅ Legacy browser support required
Real-World Usage
Where Solid.js is used:
- Financial dashboards (continuous real-time data updates)
- E-commerce sites (large product lists need speed)
- Tech blog platforms (SEO matters)
- Data visualization tools (complex charts)
Migrating from React to Solid.js:
- Build new features in Solid.js
- Start with performance-critical pages
- Use micro-frontend approach with both frameworks
You don’t have to switch everything at once – try it incrementally!
10. Deploying Your App
Deploying to Vercel
# 1. Install Vercel CLI
npm i -g vercel
# 2. Deploy from project root
vercel
# 3. Production deployment
vercel --prod
app.config.ts configuration:
import { defineConfig } from "@solidjs/start/config";
import vercel from "solid-start-vercel";
export default defineConfig({
server: {
preset: "vercel"
}
});
Deploying to Cloudflare Pages
// app.config.ts
import { defineConfig } from "@solidjs/start/config";
import cloudflare from "solid-start-cloudflare-pages";
export default defineConfig({
server: {
preset: "cloudflare-pages"
}
});
Connect to your GitHub repo for automatic deployments.
Deploying to Netlify
// app.config.ts
import { defineConfig } from "@solidjs/start/config";
import netlify from "solid-start-netlify";
export default defineConfig({
server: {
preset: "netlify",
// For Edge Functions
edge: true
}
});
Static Site Generation (SSG)
// app.config.ts
import { defineConfig } from "@solidjs/start/config";
export default defineConfig({
server: {
preset: "static"
}
});
After building, host the dist/public folder anywhere.
11. Performance Optimization Tips
1. Avoid Unnecessary Effects
// ❌ Bad: Unnecessary effect
function BadExample() {
const [count, setCount] = createSignal(0);
const [doubled, setDoubled] = createSignal(0);
createEffect(() => {
setDoubled(count() * 2); // Unnecessary signal!
});
return <div>{doubled()}</div>;
}
// ✅ Good: Use memo
function GoodExample() {
const [count, setCount] = createSignal(0);
const doubled = createMemo(() => count() * 2);
return <div>{doubled()}</div>;
}
2. Optimize Conditional Rendering
// ❌ Bad: Unnecessary recreation
function BadConditional() {
const [show, setShow] = createSignal(false);
return (
<div>
{show() && <ExpensiveComponent />}
</div>
);
}
// ✅ Good: Use Show component
function GoodConditional() {
const [show, setShow] = createSignal(false);
return (
<Show when={show()}>
<ExpensiveComponent />
</Show>
);
}
3. Optimize List Rendering
// ❌ Bad: Using map
function BadList() {
const [items, setItems] = createSignal([1, 2, 3]);
return (
<ul>
{items().map(item => <li>{item}</li>)}
</ul>
);
}
// ✅ Good: Use For component
function GoodList() {
const [items, setItems] = createSignal([1, 2, 3]);
return (
<ul>
<For each={items()}>
{(item) => <li>{item}</li>}
</For>
</ul>
);
}
4. Handling Large Lists: Virtualization
import { createVirtualizer } from "@tanstack/solid-virtual";
import { createSignal, For } from "solid-js";
function VirtualizedList() {
const [items] = createSignal(
Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
);
let parentRef;
const virtualizer = createVirtualizer({
get count() {
return items().length;
},
getScrollElement: () => parentRef,
estimateSize: () => 35,
});
return (
<div ref={parentRef} style={{ height: "400px", overflow: "auto" }}>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
position: "relative"
}}
>
<For each={virtualizer.getVirtualItems()}>
{(virtualRow) => (
<div
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`
}}
>
{items()[virtualRow.index]}
</div>
)}
</For>
</div>
</div>
);
}
5. Image Optimization
function OptimizedImage(props) {
const [loaded, setLoaded] = createSignal(false);
return (
<div class="image-container">
<Show when={!loaded()}>
<div class="skeleton" />
</Show>
<img
src={props.src}
alt={props.alt}
loading="lazy" // Browser native lazy loading
onLoad={() => setLoaded(true)}
style={{ display: loaded() ? "block" : "none" }}
/>
</div>
);
}
12. Debugging and Development Tools
Enabling Dev Mode
# Run in development mode
npm run dev
Debugging Tips
import { createSignal, createEffect } from "solid-js";
function DebugComponent() {
const [count, setCount] = createSignal(0);
// Track signal changes
createEffect(() => {
console.log("count changed:", count());
});
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
{count()}
</button>
</div>
);
}
Browser Extensions
Solid.js-specific DevTools are still limited, but you can use:
- React DevTools (partial support for structure viewing)
- Chrome Performance tab (performance profiling)
- Solid DevTools (in development)
13. Common Beginner Mistakes!
Mistake 1: Not Calling Signals as Functions
This is super common. Coming from React useState, it’s easy to forget the parentheses.
// ❌ Wrong
const [count, setCount] = createSignal(0);
console.log(count); // Outputs [Function]
return <div>{count}</div>; // Shows function object 😱
// ✅ Correct
console.log(count()); // Outputs 0
return <div>{count()}</div>; // Shows the value ✨
Why this design? Signals are functions for automatic dependency tracking. When you call count(), Solid knows “this part uses count.”
Mistake 2: Using async/await Directly in Effects
// ❌ Wrong - reactivity won't work properly
createEffect(async () => {
const data = await fetch('/api/users');
console.log(data);
});
// ✅ Solution 1: Wrap in inner function
createEffect(() => {
(async () => {
const data = await fetch('/api/users');
const json = await data.json();
console.log(json);
})();
});
// ✅✅ Solution 2: Use createResource (better!)
const [users] = createResource(async () => {
const data = await fetch('/api/users');
return data.json();
});
// Loading state handled automatically
return (
<Show when={!users.loading} fallback={<div>Loading...</div>}>
<div>{users().length} users</div>
</Show>
);
Mistake 3: Mutating Store Directly
import { createStore } from "solid-js/store";
const [todos, setTodos] = createStore([
{ id: 1, text: "Learn Solid.js", done: false }
]);
// ❌ Wrong - breaks reactivity
todos[0].done = true; // Screen won't update!
todos.push({ id: 2, text: "Build app" }); // This won't work either
// ✅ Correct - use setState function
setTodos(0, "done", true); // Mark first todo as done
setTodos([...todos, { id: 2, text: "Build app" }]); // Add new todo
// ✅✅ More convenient - find by condition
setTodos(
(todo) => todo.id === 1, // Condition
"done", // Property
true // Value
);
Mistake 4: Using map Instead of For
// ❌ Wrong - inefficient
function TodoList() {
const [todos, setTodos] = createSignal([...]);
return (
<ul>
{todos().map(todo => (
<li>{todo.text}</li>
))}
</ul>
);
}
// ✅ Correct - use For component
import { For } from "solid-js";
function TodoList() {
const [todos, setTodos] = createSignal([...]);
return (
<ul>
<For each={todos()}>
{(todo) => <li>{todo.text}</li>}
</For>
</ul>
);
}
Why is For better? map re-renders the entire list when the array changes, while For updates only changed items. Huge difference with many items!
Mistake 5: Overusing createEffect
// ❌ Wrong - effect not needed
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
const [fullName, setFullName] = createSignal("");
createEffect(() => {
setFullName(firstName() + " " + lastName()); // Unnecessary signal!
});
// ✅ Correct - use createMemo or derived function
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
// Option 1: Simple function
const fullName = () => firstName() + " " + lastName();
// Option 2: createMemo (when calculation is expensive)
const fullName = createMemo(() => firstName() + " " + lastName());
When should you use Effect?
- Direct DOM manipulation
- Console logging
- Integrating external libraries
- Saving to localStorage
Don’t use it to copy signal values to other signals!
14. Useful Libraries to Use with Solid.js
Solid.js ecosystem isn’t as big as React yet, but it has most of what you need. Here are some I’ve tried and recommend.
UI Component Libraries
Hope UI (https://hope-ui.com/)
- Similar to React’s Chakra UI
- Dark mode support is really convenient
- Great accessibility
Solid UI (https://www.solid-ui.com/)
- Shadcn/ui style components
- Copy-paste approach makes customization easy
Kobalte (https://kobalte.dev/)
- Recommend if accessibility is important
- Perfect screen reader support
Form Handling
Modular Forms (https://modularforms.dev/)
- Try this for form validation
- Works similar to React Hook Form
- Great type safety
npm install @modular-forms/solid
import { createForm } from "@modular-forms/solid";
function LoginForm() {
const [loginForm, { Form, Field }] = createForm();
return (
<Form>
<Field name="email">
{(field, props) => (
<input {...props} type="email" placeholder="Email" />
)}
</Field>
<button type="submit">Login</button>
</Form>
);
}
Styling
Tailwind CSS – Most recommended!
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
UnoCSS – Faster Tailwind alternative
- Build speed is incredibly fast
- Almost identical syntax to Tailwind
Persisting State to localStorage
npm install @solid-primitives/storage
import { makePersisted } from "@solid-primitives/storage";
// Automatically saves to localStorage
const [theme, setTheme] = makePersisted(
createSignal("light"),
{ name: "theme" }
);
// Persists even after page refresh!
Animations
Solid Transition Group
npm install solid-transition-group
import { TransitionGroup } from "solid-transition-group";
<TransitionGroup name="slide-fade">
<For each={items()}>
{(item) => <div>{item}</div>}
</For>
</TransitionGroup>
Testing
npm install --save-dev @solidjs/testing-library @testing-library/jest-dom vitest
import { render, screen } from "@solidjs/testing-library";
import { expect, test } from "vitest";
test("counter increments on button click", () => {
const { container } = render(() => <Counter />);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("0");
button.click();
expect(button).toHaveTextContent("1");
});
Icons
Solid Icons (https://solid-icons.vercel.app/)
npm install solid-icons
import { FiHeart } from 'solid-icons/fi'; // Feather Icons
import { AiFillHeart } from 'solid-icons/ai'; // Ant Design
<FiHeart size={24} color="#FF0000" />
Charts
Solid ChartJS
npm install chart.js solid-chartjs
Solid ApexCharts – Prettier charts
npm install apexcharts solid-apexcharts
15. Community and Learning Resources
Official Resources
- Official Website: https://www.solidjs.com/
- Official Docs: https://docs.solidjs.com/
- Interactive Tutorial: https://www.solidjs.com/tutorial
- Playground: https://playground.solidjs.com/
- GitHub: https://github.com/solidjs/solid
Community
- Discord: Solid official Discord server (most active)
- Reddit: r/solidjs
- Stack Overflow: #solidjs tag
16. Solid.js 2.0: Future Development
Solid 2.0 is under development with improvements in several key areas. Currently being developed on the #next branch with these goals:
Major Improvements
- Enhanced Props Handling: More intuitive props destructuring
- Partial Hydration: Improved Islands Architecture
- Server Components: Similar to React Server Components
- Better Streaming SSR: Faster initial loading
- Improved TypeScript Support: Better type inference
Track development progress at GitHub Discussion.
17. Why Solid.js Matters in 2025
Looking at 2025 web development trends, several important changes stand out:
1. Mobile-First Era 60% of global web traffic comes from mobile. Light bundle sizes and fast loading are essential – Solid.js’s 11kb bundle is ideal.
2. Performance is Competitive Advantage Google uses Core Web Vitals as a ranking factor. Studies show 1-second slower loading reduces conversion rates by 7%.
3. Developer Experience Priority Easy-to-learn, high-productivity tools are preferred. Solid.js can be learned in a day by React developers.
4. Rise of Real-Time Applications More apps need real-time updates (chat, collaboration tools, dashboards). Fine-Grained Reactivity is optimized for this.
Wrapping Up
Solid.js is more than just a “fast framework” – it’s a tool that makes web development more enjoyable. It proves you can be more efficient without Virtual DOM, and React developers can learn it easily.
This doesn’t mean you should abandon React immediately. React is still an excellent framework with a massive ecosystem and countless libraries. But if you’re starting a new project or building performance-critical services, Solid.js is definitely worth considering.
Quick Start Commands
# Create new project
npm create solid@latest my-app
# Start dev server
npm run dev
# Build for production
npm run build
When you get stuck:
- Ask on the official Discord (people are friendly!)
- Check examples in official docs
- Experiment in the Playground
Things to remember:
- Always call Signals as functions:
count()⭕ /count❌ - Use Effects only when necessary
- Render lists with the For component
How about trying Solid.js on your next project? The official tutorial takes just 30 minutes to learn the basics. You’ll be amazed when you experience the performance difference yourself! 😊