SolidJS compiles to direct DOM updates — no virtual DOM diffing. SolidStart adds routing, SSR, and API routes on top. The result? React's DX with 10x better performance.
What is SolidStart?
SolidStart is the full-stack framework for SolidJS. It handles server-side rendering, file-based routing, API routes, and server functions — powered by Vinxi and Nitro under the hood.
Why SolidStart
1. Fine-Grained Reactivity
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
// This div NEVER re-renders. Only the text node updates.
return (
<div>
<p>Count: {count()}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
No virtual DOM. No component re-renders. Only the exact DOM node that changed updates.
2. Server Functions
"use server";
async function getUser(id: string) {
return await db.users.findUnique({ where: { id } });
}
async function createPost(title: string, content: string) {
return await db.posts.create({ data: { title, content } });
}
// Use directly in components
function UserProfile(props: { userId: string }) {
const user = createAsync(() => getUser(props.userId));
return <h1>{user()?.name}</h1>;
}
3. File-Based Routing
src/routes/
├── index.tsx → /
├── about.tsx → /about
├── blog/
│ ├── index.tsx → /blog
│ └── [slug].tsx → /blog/:slug
├── api/
│ └── users.ts → /api/users
└── (auth)/
├── login.tsx → /login
└── register.tsx → /register
4. Data Loading with createAsync
import { createAsync, cache } from "@solidjs/router";
const getProducts = cache(async () => {
"use server";
return await db.products.findMany({ where: { active: true } });
}, "products");
export default function Products() {
const products = createAsync(() => getProducts());
return (
<For each={products()}>
{(product) => (
<div>
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
)}
</For>
);
}
5. Actions (Form Handling)
import { action, redirect } from "@solidjs/router";
const createTodo = action(async (formData: FormData) => {
"use server";
const title = formData.get("title") as string;
await db.todos.create({ data: { title } });
throw redirect("/todos");
});
export default function NewTodo() {
return (
<form action={createTodo} method="post">
<input name="title" required />
<button type="submit">Create</button>
</form>
);
}
Performance
| Benchmark | SolidStart | Next.js | SvelteKit |
|---|---|---|---|
| Bundle size (hello) | 7KB | 85KB | 15KB |
| Update perf (JS Framework Bench) | #1 | #15 | #5 |
| Memory usage | Low | High | Medium |
SolidStart vs Next.js vs SvelteKit
| SolidStart | Next.js | SvelteKit | |
|---|---|---|---|
| Reactivity | Fine-grained signals | Virtual DOM | Compiled |
| Re-renders | None (surgical updates) | Component-level | Component-level |
| Server functions | "use server" | Server actions | +page.server.ts |
| JSX | Yes (compiled) | Yes (virtual DOM) | No (Svelte syntax) |
| Learning curve | Low (React-like) | Low | Low |
Getting Started
npm init solid@latest my-app
# Choose: SolidStart
cd my-app
npm install
npm run dev
The Bottom Line
SolidStart proves you don't need a virtual DOM for a great developer experience. React's syntax with surgical DOM updates, full-stack capabilities, and the best raw performance of any framework.
Need data tools? I build scraping solutions. Check my Apify actors or email spinov001@gmail.com.
Top comments (0)