Remix hat sich 2026 als ernsthafter Konkurrent zu Next.js etabliert — besonders für Teams, die serverseitiges Rendering, progressive Enhancement und eine saubere Datenschicht ohne REST-Overhead bevorzugen. Claude Code beschleunigt die Remix-Entwicklung erheblich: Routen, Loader und Actions entstehen in Minuten statt Stunden.
Was du in diesem Artikel lernst
- Remix Route-Struktur: Nested Routes und Layout Routes aufbauen
- Loader für serverseitiges Datenfetching ohne useEffect
- Actions für Mutations — sauber, ohne Client-State
- Progressive Enhancement: Forms die ohne JS funktionieren
- Error Boundaries und Catch Boundaries richtig einsetzen
- Remix vs Next.js: Entscheidungsmatrix für 2026
- Deployment auf Vercel, Fly.io und Cloudflare Workers
1. Remix Route-Struktur: Nested Routes & Layout Routes
Remix setzt auf dateisystem-basiertes Routing. Jede Datei im app/routes/-Verzeichnis wird eine Route. Das Besondere: Nested Routes ermöglichen echtes Layout-Sharing mit isoliertem Datenfetching pro Segment.
app/
routes/
_index.tsx // → /
dashboard.tsx // Layout für /dashboard/*
dashboard._index.tsx // → /dashboard
dashboard.projects.tsx // → /dashboard/projects
dashboard.projects.$id.tsx // → /dashboard/projects/:id
_auth.tsx // Layout ohne URL-Segment
_auth.login.tsx // → /login
_auth.register.tsx // → /register
Das dashboard.tsx-Layout rendert ein <Outlet /> — Kind-Routen werden dort eingebettet. Claude Code erkennt automatisch die Route-Hierarchie und generiert passende Layout-Komponenten:
import { Outlet, NavLink } from "@remix-run/react"; export default function DashboardLayout() { return ( <div className="dashboard"> <nav className="sidebar"> <NavLink to="/dashboard" end>Übersicht</NavLink> <NavLink to="/dashboard/projects">Projekte</NavLink> </nav> <main> <Outlet /> // Kind-Route wird hier gerendert </main> </div> ); }
2. Loader: Serverseitiges Datenfetching ohne useEffect
Der loader ersetzt useEffect + fetch. Er läuft ausschließlich auf dem Server, hat Zugriff auf Cookies, Sessions und Datenbanken — und gibt typisierte Daten an die Komponente zurück.
import { json, type LoaderFunctionArgs } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { db } from "~/lib/db.server"; import { requireUser } from "~/lib/auth.server"; export async function loader({ request }: LoaderFunctionArgs) { const user = await requireUser(request); // Session prüfen const projects = await db.project.findMany({ where: { userId: user.id }, orderBy: { updatedAt: "desc" }, }); return json({ projects, user }); } export default function Projects() { const { projects } = useLoaderData<typeof loader>(); return ( <ul> {projects.map((p) => ( <li key={p.id}>{p.name}</li> ))} </ul> ); }
Kein Redux, kein React Query, kein SWR notwendig. Der Loader erledigt Caching, Revalidierung und Typinferenz direkt — useLoaderData<typeof loader>() ist vollständig typisiert.
3. Actions: Server-seitige Mutations
Actions sind das Gegenstück zu Loaders: Sie verarbeiten POST/PUT/DELETE-Requests. Claude Code generiert Actions mit Validierung, Fehlerbehandlung und Redirect:
import { redirect, json, type ActionFunctionArgs } from "@remix-run/node"; import { Form, useActionData } from "@remix-run/react"; import { z } from "zod"; const ProjectSchema = z.object({ name: z.string().min(3).max(80), description: z.string().optional(), }); export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const raw = Object.fromEntries(formData); const result = ProjectSchema.safeParse(raw); if (!result.success) { return json({ errors: result.error.flatten() }, { status: 400 }); } const project = await db.project.create({ data: result.data }); return redirect(`/dashboard/projects/${project.id}`); } export default function NewProject() { const actionData = useActionData<typeof action>(); return ( <Form method="post"> <input name="name" placeholder="Projektname" /> {actionData?.errors?.fieldErrors?.name && ( <p className="error">{actionData.errors.fieldErrors.name}</p> )} <button type="submit">Erstellen</button> </Form> ); }
4. Progressive Enhancement: Forms ohne JavaScript
Remixs <Form>-Komponente ist ein natives HTML-Form mit JavaScript-Enhancement. Wenn JS geladen ist, wird die Submission per fetch abgewickelt. Wenn nicht — funktioniert das Form trotzdem als Standard-HTML-POST.
import { Form, useNavigation, useSubmit } from "@remix-run/react"; export default function TaskForm() { const navigation = useNavigation(); const isSubmitting = navigation.state === "submitting"; return ( // method="post" → native HTML fallback wenn kein JS <Form method="post" action="/tasks/new"> <input name="title" required placeholder="Task-Titel" disabled={isSubmitting} /> <button type="submit" disabled={isSubmitting}> {isSubmitting ? "Wird gespeichert..." : "Speichern"} </button> </Form> ); } // Optimistic UI mit useFetcher: import { useFetcher } from "@remix-run/react"; function LikeButton({ postId }: { postId: string }) { const fetcher = useFetcher(); const liked = fetcher.formData ? fetcher.formData.get("liked") === "true" : false; return ( <fetcher.Form method="post" action=`/posts/${postId}/like`> <input type="hidden" name="liked" value="true" /> <button>{liked ? "❤️" : "🤍"}</button> </fetcher.Form> ); }
5. Error Boundaries & Catch Boundaries
Remix hat eingebaute Error-Isolation pro Route. Fehler "bubblen" nicht die gesamte App hoch — nur die betroffene Route zeigt den Fehler, der Rest bleibt funktionsfähig.
import { isRouteErrorResponse, useRouteError } from "@remix-run/react"; // Eine ErrorBoundary Funktion für alle Fehlertypen (Remix v2+) export function ErrorBoundary() { const error = useRouteError(); if (isRouteErrorResponse(error)) { // HTTP-Fehler: 404, 401, 403... return ( <div className="error-page"> <h1>{error.status} {error.statusText}</h1> <p>{error.data}</p> </div> ); } if (error instanceof Error) { // Unerwartete JavaScript-Fehler return ( <div className="error-page"> <h1>Unerwarteter Fehler</h1> <p>{error.message}</p> </div> ); } return <h1>Unbekannter Fehler</h1>; } // Im Loader: throw Response für HTTP-Fehler export async function loader({ params }: LoaderFunctionArgs) { const project = await db.project.findUnique({ where: { id: params.id } }); if (!project) { throw new Response("Projekt nicht gefunden", { status: 404 }); } return json({ project }); }
6. Remix vs Next.js: Wann Remix wählen?
Beide Frameworks sind 2026 produktionsreif. Die Wahl hängt vom Use Case ab:
| Kriterium | Remix | Next.js 15 |
|---|---|---|
| Datenfetching-Modell | Loader/Action (klar getrennt) | Server Components + fetch |
| Form Handling | Native, progressive Enhancement | Server Actions (ähnlich) |
| Nested Layouts | Built-in, pro Route | App Router layouts |
| Caching-Kontrolle | Manuell via Headers | Granulares Cache-API |
| Edge / Cloudflare Workers | Erstklassig unterstützt | Edge Runtime (Einschränkungen) |
| Static Site Generation | Nicht der Fokus | Vollständig unterstützt |
| Learning Curve | Weniger Konzepte | App Router komplex |
| Claude Code Support | Excellent | Excellent |
Wann Remix wählen?
- Apps mit viel User-Interaktion und Formularen (CRMs, Dashboards, SaaS)
- Progressive Enhancement als Anforderung (Accessibility, schlechte Verbindungen)
- Deployment auf Cloudflare Workers oder Fly.io
- Teams die explizite Server/Client-Trennung bevorzugen
- Wenn kein separater API-Layer gewünscht wird
7. Deployment: Vercel, Fly.io & Cloudflare Workers
Remix unterstützt multiple Deployment-Targets über Adapter. Claude Code konfiguriert den passenden Adapter automatisch:
Vercel
Einfachster Start. @remix-run/vercel Adapter, automatisches Deployment via Git Push. Serverless Functions pro Route.
Fly.io
Persistente Server, Datenbanken nah am User. Ideal für Supabase + Remix Kombinationen. Docker-basiert, globales Edge.
Cloudflare Workers
Schnellstes Cold-Start. @remix-run/cloudflare Adapter. KV, Durable Objects, D1 direkt nutzbar.
// Vercel Deployment import { vitePlugin as remix } from "@remix-run/dev"; import { vercelPreset } from "@vercel/remix/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ remix({ presets: [vercelPreset()] }), ], }); // Cloudflare Workers import { cloudflareDevProxyVitePlugin, vitePlugin as remix, } from "@remix-run/dev"; export default defineConfig({ plugins: [ cloudflareDevProxyVitePlugin(), remix({ future: { v3_fetcherPersist: true }, }), ], }); // Fly.io: Dockerfile + fly.toml // fly launch --name meine-remix-app --region fra
Claude Code als Remix-Entwicklungspartner
Claude Code versteht Remixs Konventionen tief. Typische Workflows die Claude Code in Minuten erledigt:
- Route-Generierung: "Erstelle eine CRUD-Route für /admin/users mit Loader, Action und Error Boundary"
- Auth-Setup: "Implementiere Session-basierte Authentifizierung mit remix-auth und Prisma"
- Optimistic UI: "Füge optimistic Updates für den Like-Button mit useFetcher hinzu"
- Migration: "Migriere diese Next.js Pages Router Seite zu einer Remix Route"
- Testing: "Schreibe Vitest-Tests für diesen Loader und diese Action"
Starte dein Remix-Projekt mit KI-Unterstützung
SpockyMagicAI integriert Claude Code direkt in deinen Entwicklungs-Workflow — von der Route-Planung bis zum Deployment. Kostenloser Trial, keine Kreditkarte nötig.
Kostenlos ausprobieren →