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.

 

Solid.js

 

 

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:

  1. 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.
  2. 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.
  3. 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, useCallback optimization 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/about
    • users/[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:

  1. Build new features in Solid.js
  2. Start with performance-critical pages
  3. 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

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

  1. Enhanced Props Handling: More intuitive props destructuring
  2. Partial Hydration: Improved Islands Architecture
  3. Server Components: Similar to React Server Components
  4. Better Streaming SSR: Faster initial loading
  5. 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! 😊

 

 

Leave a Reply