SolidJS & Framework

SolidJS mit Claude Code:
Reaktivität ohne Virtual DOM 2026

createSignal, createMemo, createEffect, Stores, SolidStart und JSX-Kompilierung — warum SolidJS schneller als React ist und wann du es einsetzen solltest.

📅 6. Mai 2026 ⏱ 10 min Lesezeit 👤 SpockyMagicAI Team

Inhalt

  1. SolidJS Grundprinzipien & Fine-grained Reaktivität
  2. Reaktivitäts-Primitives: createMemo, createEffect, createResource
  3. Stores & Mutation: createStore, produce, reconcile
  4. Control-Flow Komponenten: Show, For, Switch, Portal
  5. SolidStart: File-based Routing & Server Functions
  6. Performance-Vergleich: Solid vs. React

Während React seit Jahren den Frontend-Markt dominiert, gewinnt SolidJS 2026 zunehmend an Bedeutung — und das aus gutem Grund. Der Kernunterschied liegt nicht nur in der Performance, sondern im fundamentalen Designprinzip: SolidJS verzichtet vollständig auf ein Virtual DOM und setzt stattdessen auf Fine-grained Reaktivität. Das Ergebnis ist ein Framework, das schneller ist als React, weniger RAM verbraucht und trotzdem eine sehr ähnliche Entwicklererfahrung bietet.

In diesem Artikel zeigen wir dir — anhand konkreter TypeScript-Beispiele — wie du SolidJS mit Claude Code produktiv nutzt: von den Grundprimitiven über komplexe Store-Mutations bis hin zu SolidStart für Full-Stack-Anwendungen.

1. SolidJS Grundprinzipien: Kein Virtual DOM

React rendert bei jeder State-Änderung den Komponenten-Baum neu und vergleicht das Ergebnis mit dem vorherigen Virtual DOM (Reconciliation). SolidJS geht einen anderen Weg: JSX wird beim Build-Schritt direkt zu imperativen DOM-Aufrufen kompiliert. Zur Laufzeit gibt es keinen Diffing-Algorithmus — Updates passieren chirurgisch, genau dort wo sie gebraucht werden.

Kernkonzept

Was passiert beim Kompilieren?

Aus diesem JSX:

const App = () => { const [count, setCount] = createSignal(0); return <button onClick={() => setCount(count() + 1)}>{count()}</button>; };

...generiert der Compiler in etwa:

const App = () => { const [count, setCount] = createSignal(0); const _el = document.createElement("button"); _el.addEventListener("click", () => setCount(count() + 1)); // createEffect verknüpft count() mit dem Textknoten createEffect(() => (_el.textContent = count())); return _el; };

Die Komponente läuft nur einmal. Danach aktualisiert nur der createEffect-Aufruf den Textknoten — direkt im DOM, ohne Re-Render.

createSignal: Der Einstieg

Das grundlegendste Primitive in SolidJS ist createSignal. Es gibt ein Tupel aus Getter und Setter zurück — ähnlich wie Reacts useState, aber mit einem entscheidenden Unterschied: Der Getter ist eine Funktion, die aufgerufen werden muss.

import { createSignal, createEffect } from "solid-js"; // Typisiertes Signal const [name, setName] = createSignal<string>("SpockyMagicAI"); const [count, setCount] = createSignal(0); // Getter = Funktion aufrufen! console.log(name()); // "SpockyMagicAI" console.log(count()); // 0 // Setter mit Wert setCount(42); // Setter mit Updater-Funktion (wie React) setCount(prev => prev + 1); // Signal-Optionen: equals verhindert unnötige Updates const [pos, setPos] = createSignal( { x: 0, y: 0 }, { equals: (a, b) => a.x === b.x && a.y === b.y } );
Claude Code Tipp Wenn du Claude Code anforderst, eine SolidJS-Komponente zu schreiben, sage explizit "SolidJS, kein React" — Claude kennt den Unterschied zwischen den Gettern und behandelt Signals korrekt als aufrufbare Funktionen, nicht als direkte Werte.

Warum kein Virtual DOM schneller ist

Das Virtual DOM war eine geniale Idee für 2013 — heute ist es ein notwendiges Übel. Jedes State-Update erzeugt einen neuen Virtual-DOM-Baum, der mit dem alten verglichen werden muss. Bei großen Komponentenbäumen ist dieser Diff teuer. SolidJS umgeht dieses Problem vollständig: Jede reaktive Abhängigkeit ist zur Compile-Zeit bekannt und wird direkt verdrahtet.

2. Reaktivitäts-Primitives: createMemo, createEffect, createResource

SolidJS bietet eine kohärente Sammlung von Primitiven für verschiedene reaktive Szenarien. Im Gegensatz zu React Hooks gibt es keine "Rules of Hooks" — Primitives können überall aufgerufen werden, solange sie sich in einem reaktiven Scope befinden.

createMemo: Berechnete Werte

import { createSignal, createMemo } from "solid-js"; const [items, setItems] = createSignal<number[]>([1, 2, 3, 4, 5]); const [filter, setFilter] = createSignal("even"); // createMemo cached das Ergebnis — wird nur neu berechnet wenn // items() oder filter() sich ändern const filteredItems = createMemo(() => { const list = items(); return filter() === "even" ? list.filter(n => n % 2 === 0) : list.filter(n => n % 2 !== 0); }); // Memo mit Gleichheitsprüfung (verhindert Re-Renders bei identischem Ergebnis) const sum = createMemo( () => items().reduce((a, b) => a + b, 0), 0, // initialValue { equals: (a, b) => a === b } ); // filteredItems() und sum() verhalten sich wie Signals — aufrufbar als Funktion console.log(filteredItems()); // [2, 4] console.log(sum()); // 15

createEffect: Seiteneffekte

import { createSignal, createEffect, on, onCleanup } from "solid-js"; const [userId, setUserId] = createSignal(1); // Einfacher Effect — tracked automatisch alle Signals die aufgerufen werden createEffect(() => { console.log("User ID geändert:", userId()); }); // Effect mit Cleanup (z.B. für Event Listener) createEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") setUserId(1); }; document.addEventListener("keydown", handler); onCleanup(() => document.removeEventListener("keydown", handler)); }); // on() für explizite Dependency-Tracking (wie useEffect deps-Array) createEffect(on(userId, (id, prevId) => { console.log(`User wechselte von ${prevId} zu ${id}`); }, { defer: true })); // defer: nicht beim ersten Mount

createResource: Async-Daten

import { createSignal, createResource } from "solid-js"; type User = { id: number; name: string; email: string }; const fetchUser = async (id: number): Promise<User> => { const res = await fetch(`https://api.example.com/users/${id}`); if (!res.ok) throw new Error("Fetch fehlgeschlagen"); return res.json(); }; const [userId, setUserId] = createSignal(1); // createResource(source, fetcher) // source = reaktive Abhängigkeit; fetcher wird neu aufgerufen wenn source sich ändert const [user, { refetch, mutate }] = createResource(userId, fetchUser); // Im Template: const UserCard = () => ( <div> <Show when={!user.loading} fallback={<Spinner />}> <Show when={!user.error} fallback={<ErrorMsg msg={user.error.message} />}> <p>{user().name}</p> <p>{user().email}</p> </Show> </Show> <button onClick={refetch}>Neu laden</button> </div> );
Fortgeschritten

batch und untrack

batch() gruppiert mehrere State-Updates zu einem einzigen reaktiven Durchlauf. untrack() liest ein Signal, ohne eine Abhängigkeit zu registrieren.

import { batch, untrack } from "solid-js"; // Ohne batch: 3 separate Updates → 3 Effect-Durchläufe // Mit batch: 1 Update → 1 Effect-Durchlauf batch(() => { setFirstName("Maximilian"); setLastName("Müller"); setAge(32); }); // untrack: Signal lesen ohne Abhängigkeit zu registrieren createEffect(() => { const currentCount = count(); // registriert Abhängigkeit const unchangedBase = untrack(baseValue); // KEIN Tracking console.log(currentCount - unchangedBase); });

3. Stores & Mutation: createStore, produce, reconcile

Für komplexe, verschachtelte State-Strukturen bietet SolidJS createStore — ein reaktiver Proxy, der granulare Updates auf tief verschachtelte Objekte ermöglicht, ohne den gesamten State zu ersetzen.

Store

createStore Grundlagen

import { createStore, produce, reconcile, unwrap } from "solid-js/store"; interface TodoItem { id: number; text: string; done: boolean; tags: string[]; } interface AppState { todos: TodoItem[]; filter: "all" | "active" | "done"; user: { name: string; theme: "light" | "dark" }; } const [state, setState] = createStore<AppState>({ todos: [], filter: "all", user: { name: "Daniel", theme: "dark" }, }); // Direkter Pfad-Update (SolidJS-spezifisch) setState("user", "theme", "light"); setState("filter", "active"); // Array-Update per Index setState("todos", 0, "done", true); // Array-Update per Filterfunktion setState("todos", (todo) => !todo.done, "tags", (tags) => [...tags, "wichtig"]); // state lesen — kein () nötig (Proxy-Objekt, kein Signal) console.log(state.user.name); // "Daniel" console.log(state.todos.length);

produce: Immer-ähnliche Mutations

import { produce } from "solid-js/store"; // produce() erlaubt direkte Mutations (wie Immer.js) setState(produce((s) => { s.todos.push({ id: Date.now(), text: "Claude Code konfigurieren", done: false, tags: ["dev"], }); s.todos .filter((t) => t.done) .forEach((t) => (t.tags = [])); }));

reconcile: Externe Daten synchronisieren

import { reconcile } from "solid-js/store"; // reconcile() diffed externe Daten gegen aktuellen Store // Ideal für API-Responses — verhindert unnötige DOM-Updates async function refreshTodos() { const fresh = await fetchTodosFromApi(); setState("todos", reconcile(fresh, { key: "id", merge: true })); // Nur wirklich geänderte Todos triggern DOM-Updates }
Store vs. Signal — wann was? Verwende Signal für einfache skalare Werte (Zahlen, Strings, Booleans) und Store für verschachtelte Objekte und Arrays. Stores sind Proxies — du kannst sie nicht destrukturieren ohne den reaktiven Kontext zu verlieren. Immer state.todo.text schreiben, nie const {todo} = state.

4. Control-Flow Komponenten

SolidJS bietet eingebaute Komponenten für reaktive Verzweigungen und Listen. Sie sind essentiell für Performance, weil sie granulare DOM-Updates ermöglichen — kein unnötiges Re-Mounting bei jeder Änderung.

<Show>: Bedingtes Rendering

import { Show } from "solid-js"; // when: beliebiger Wert (wird truthy geprüft) // fallback: optional, was gezeigt wird wenn when falsy // keyed: wenn true, unmountet Kinder bei jeder when-Änderung (Default: false) const UserView = () => ( <Show when={isLoggedIn()} fallback={<LoginForm />} keyed > {(user) => <Dashboard user={user} />} </Show> );

<For>: Listen-Rendering

import { For, Index } from "solid-js"; // For: Item-basiert — behält DOM-Knoten wenn sich Reihenfolge ändert // Ideal für Arrays von Objekten (referenzielle Gleichheit) const TodoList = () => ( <ul> <For each={state.todos} fallback={<li>Keine Todos vorhanden.</li>}> {(todo, index) => ( <li> <input type="checkbox" checked={todo.done} onChange={() => setState("todos", index(), "done", (d) => !d)} /> <span>{todo.text}</span> <For each={todo.tags}> {(tag) => <span class="tag">{tag}</span>} </For> </li> )} </For> </ul> ); // Index: Position-basiert — Ideal für primitive Arrays (Strings, Zahlen) const NumberList = () => ( <Index each={[1, 2, 3]}> {(item, i) => <span>{i}: {item()}</span>} </Index> );

<Switch>/<Match>, <Dynamic>, <Portal>, <ErrorBoundary>

import { Switch, Match, Dynamic, Portal, ErrorBoundary } from "solid-js"; // Switch + Match: Multi-Branch const StatusView = () => ( <Switch fallback={<UnknownStatus />}> <Match when={status() === "loading"}><Spinner /></Match> <Match when={status() === "error"}><ErrorBanner /></Match> <Match when={status() === "success"}><DataView /></Match> </Switch> ); // Dynamic: Komponente zur Laufzeit wählen const componentMap = { card: CardView, list: ListView, grid: GridView }; const DynamicView = () => ( <Dynamic component={componentMap[viewMode()]} data={items()} /> ); // Portal: DOM außerhalb des Komponenten-Baums const Modal = () => ( <Portal mount={document.getElementById("modal-root")!}> <div class="modal-overlay">...</div> </Portal> ); // ErrorBoundary: Fehlerbehandlung const SafeApp = () => ( <ErrorBoundary fallback={(err, reset) => ( <div> <p>Fehler: {err.message}</p> <button onClick={reset}>Neu versuchen</button> </div> )} > <ChildThatMightThrow /> </ErrorBoundary> );
Claude Code Tipp Frag Claude Code: "Erstelle eine SolidJS-Komponente mit <For> die eine Liste von Produkten rendert und <Show> für den Ladezustand nutzt." Claude generiert korrekte Solid-Syntax inklusive Getter-Aufrufen und prop-drilling-freier Stores.

5. SolidStart: File-based Routing & Server Functions

SolidStart

Was ist SolidStart?

SolidStart ist das offizielle Full-Stack-Framework für SolidJS — vergleichbar mit Next.js für React oder Nuxt für Vue. Es bietet file-based Routing, Server Functions, SSR/SSG und Streaming.

File-based Routing

// Projektstruktur: src/ routes/ index.tsx → / about.tsx → /about blog/ index.tsx → /blog [slug].tsx → /blog/:slug (dynamische Route) [...rest].tsx → /blog/* (Catch-all) (auth)/ → Layout-Gruppe (kein URL-Segment) login.tsx → /login register.tsx → /register app.tsx → Root Layout
// src/routes/blog/[slug].tsx import { createAsync, useParams } from "@solidjs/router"; import { getPost } from "~/lib/posts"; // Server Function // createAsync: reaktive Async-Daten (kombiniert createResource mit Routing) export default function BlogPost() { const params = useParams(); const post = createAsync(() => getPost(params.slug)); return ( <Show when={post()} fallback={<PostSkeleton />}> {(p) => ( <article> <h1>{p.title}</h1> <div innerHTML={p.html} /> </article> )} </Show> ); }

Server Functions: "use server"

// src/lib/posts.ts "use server"; // Diese Datei läuft NUR auf dem Server import { db } from "~/lib/db"; import { getRequestEvent } from "solid-js/web"; export async function getPost(slug: string) { // Direkter DB-Zugriff — kein API-Layer nötig const post = await db.post.findFirst({ where: { slug } }); if (!post) throw new Error("Post nicht gefunden"); return post; } export async function createPost(data: FormData) { "use server"; // Kann auch inline in einer Funktion stehen const event = getRequestEvent(); // Zugriff auf Request/Response const session = await getSession(event); if (!session.userId) throw new Error("Nicht eingeloggt"); return db.post.create({ data: { title: data.get("title") as string, content: data.get("content") as string, authorId: session.userId, }, }); }

Middleware & Cache

// src/middleware.ts import { createMiddleware } from "@solidjs/start/middleware"; export default createMiddleware({ onRequest: [ async ({ request, forward }) => { // Auth-Header prüfen const token = request.headers.get("Authorization"); if (!token) return new Response("Unauthorized", { status: 401 }); return forward(); }, ], }); // Cache für Server Functions import { cache } from "@solidjs/router"; const getCachedPost = cache(getPost, "post"); // Cache-Key "post" // Automatisch dedupliziert bei gleichzeitigen Requests // Cache wird geleert bei server-seitigen Mutations (action())

6. Performance-Vergleich: Solid vs. React

Performance

Wie funktioniert Fine-grained Reaktivität?

Wenn du in React setState aufrufst, wird die gesamte Komponente (und standardmäßig alle Kind-Komponenten) neu gerendert. SolidJS erstellt stattdessen beim ersten Render einen Dependency-Graph: Jeder reaktive Ausdruck (Effect, Memo, DOM-Binding) weiß exakt, von welchen Signals er abhängt. Änderungen propagieren nur entlang dieser Kanten.

React Re-render vs. Solid Fine-grained Update

// React: Gesamte Komponente rendert neu function ReactCounter() { const [count, setCount] = useState(0); console.log("Render!"); // wird bei JEDEM setCount aufgerufen return ( <div> <p>{count}</p> <button onClick={() => setCount(c => c + 1)}>+</button> <ExpensiveChild /> // rendert AUCH neu (außer React.memo) </div> ); } // SolidJS: Nur der <p>-Textknoten ändert sich function SolidCounter() { const [count, setCount] = createSignal(0); console.log("Render!"); // wird NUR EINMAL beim ersten Mount aufgerufen return ( <div> <p>{count()}</p> {/* nur dieser DOM-Knoten updatet */} <button onClick={() => setCount(c => c + 1)}>+</button> <ExpensiveChild /> // NIEMALS neu gemounted, kein React.memo nötig </div> ); }

Benchmark-Vergleich (js-framework-benchmark, 2026)

Framework Create 1k rows (ms) Update 1k rows (ms) Select row (ms) Memory (MB)
SolidJS 1.9 41.2 28.7 5.1 3.2
Svelte 5 44.8 31.2 5.8 3.9
Vue 3.5 52.1 38.4 6.9 5.1
React 19 68.3 58.9 9.2 7.4
Angular 18 74.1 62.3 10.4 8.8
Benchmarks richtig lesen Diese Zahlen sind synthetische Benchmarks — in realen Applikationen ist der Unterschied kleiner. Entscheidend ist: SolidJS ist konsistent schneller als React, besonders bei häufigen, granularen Updates (Echtzeit-Daten, Live-Charts, kollaborative Tools).

Wann SolidJS wählen?

Szenario SolidJS React
Echtzeit-Dashboards, Live-Daten Ideal Möglich (mit Optimierungen)
Performance-kritische SPAs Ideal Gut mit React.memo/useMemo
Großes Team, viel Erfahrung Lernkurve Breites Ökosystem
Full-Stack mit SSR/SSG SolidStart Next.js
Mobile (React Native) Kein Äquivalent React Native
Greenfield-Projekt, 2026 Sehr empfehlenswert Bewährt, sicher
Migration bestehende React-App Hoher Aufwand Status quo
Claude Code & SolidJS — Workflow-Empfehlung Claude Code versteht SolidJS nativ. Nutze es für: Komponenten-Scaffolding ("erstelle einen SolidStart API-Route Handler"), Store-Design ("entwirf einen createStore für diesen Datentyp") und Performance-Analyse ("erkläre warum dieser Effect unnötig triggert"). Die KI erkennt typische Solid-Antipatterns wie das Destrukturieren von Store-Objekten oder das vergessene () beim Getter.
SolidJS TypeScript Fine-grained Reactivity SolidStart createSignal createStore Frontend 2026 Claude Code JavaScript Frameworks Virtual DOM

SolidJS mit KI-Unterstützung entwickeln

Starte deinen kostenlosen Trial und lass Claude Code deine SolidJS-Projekte beschleunigen — von der Komponentenstruktur bis zum SolidStart-Deployment.

Jetzt kostenlos testen →