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.

Verzeichnisstruktur
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:

dashboard.tsx — Layout Route
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.

Claude Code Prompt: "Erstelle einen Remix Loader für /dashboard/projects der alle Projekte aus Supabase lädt und nach Status filtert. Nutze json() helper und TypeScript."
dashboard.projects.tsx
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:

dashboard.projects.new.tsx
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.

Progressive Enhancement Pattern
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.

Error Boundaries in Remix
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.

remix.config.js — Adapter wählen
// 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:

Tipp: Claude Code mit Remix kombinieren bedeutet: Projektstruktur einmalig erklären, dann Routen auf Zuruf generieren lassen. Ein erfahrener Entwickler überprüft, ein Junior implementiert — Claude Code beschleunigt beide um Faktor 3-5x.

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 →