Geschrieben von Huifer, Solo-Entwickler und Maintainer von TanStack Ship. Ich entwickle TanStack Ship, weil ich es selbst nutze – zwölf Produktions-SaaS-Apps und weitere seit 2024, alle auf diesem exakten Stack. Jedes Feature unten habe ich persönlich verdrahtet, debuggt, ersetzt oder sonntagnachts um 2 Uhr rausgerissen. Es gibt kein Marketing-Übertreibung und keine Roadmap-Spekulationen; wenn ein Feature holprig ist, sage ich das. TanStack Ship ist mein kommerzielles Produkt, und dieser Bias ist hiermit offengelegt.
Verifizierte Quellen: TanStack Start docs · TanStack Router docs · TanStack Query docs · Cloudflare D1 · Cloudflare R2 · Cloudflare Queues · Cloudflare Durable Objects · Stripe Subscriptions · Resend · TanStack Ship GitHub
Last updated: 2026-08-05 · Changelog
TL;DR: TanStack Ship ist ein TanStack Start + Cloudflare Workers Boilerplate für den Solo-Launch von Produktions-SaaS. Der 2026 Stack Layer ist TanStack Start, TanStack Router, TanStack Query, TanStack Form, Drizzle ORM, Better-Auth, Stripe, Resend, shadcn/ui, Cloudflare D1/R2/KV/Queues/Durable Objects und Workers AI. Die Features, die wirklich relevant sind, sind die langweiligen: file-basiertes typsicheres Routing, ein echtes Billing-Modul mit Webhook-Idempotenz, eine Edge-native Datenschicht, eine transaktionale E-Mail-Pipeline mit Queue-Retries und Observability-Hooks, die Sie alarmieren, bevor es Ihre Kunden tun. Die Features, die ich 2026 noch als holprig bezeichnen würde: langlebige Hintergrundjobs, die das 15-Minuten-Queue-Budget überschreiten, Multi-Region Durable Object-Koordination und das shadcn/ui-Komponenten-Drift-Problem.
Was TanStack Ship wirklich ist – und was es nicht ist
TanStack Ship ist kein generischer Next.js-Starter mit einer Marketing-Seite. Es ist ein TanStack Start + Cloudflare Workers Boilerplate, der auf eine ganz bestimmte Persona ausgerichtet ist: Solo-Entwickler oder Zwei-Personen-Teams, die ein bezahltes SaaS-Produkt in Tagen statt Monaten auf den Markt bringen müssen. Es wird mit der grundlegenden, aber entscheidenden Infrastruktur geliefert, die bereits verdrahtet ist – Abrechnung, Authentifizierung, E-Mail, Observability – sodass Sie nur Ihr eigentliches Produkt entwickeln.
Wenn Sie eine Marketing-Site, ein internes Tool oder ein Hobbyprojekt bauen, ist das hier Overkill und ich sage es offen. Wenn Sie ein B2B-SaaS mit einem Free-Tier, kostenpflichtigen Abonnements, einem Postgres-förmigen Datenmodell (das D1 2026 gut handhabt) bauen und damit rechnen, um 3 Uhr nachts Bereitschaft zu haben, wenn etwas kaputtgeht – das ist der Stack, den ich zuerst nehme. Das vollständige Feature-Inventar finden Sie auf /features; den Vergleich mit anderen Startern auf /compare; wenn Sie gegen ShipFast, Makerkit oder Supastarter evaluieren, finden Sie diese Aufschlüsselungen auf /alternatives.
Der Rest dieses Artikels durchläuft jede Ebene des Stacks mit Begründung, Code-Form und den Grenzen, die ich in der Produktion erreicht habe.
Die Stack-Ebene – TanStack Start, Router, Query, Form
Die TanStack-Familie ist das tragende Element. Alles andere im Boilerplate kann ausgetauscht werden; TanStack ist der Teil, den ich nach drei Jahren Nutzung nicht ersetzen werde.
File-basiertes Routing, das tatsächlich typsicher ist
TanStack Routers file-basierte Routes geben Ihnen die Developer Experience, die Next.js 2017 versprochen und nie ganz geliefert hat: Jede URL ist eine Datei, jede Datei generiert Typen, und jede Navigation innerhalb der App wird zur Kompilierzeit typgeprüft. Der Boilerplate wird mit dem vorgefertigten Route-Tree und einem Route-Loader geliefert, der an React Suspense angeschlossen ist, sodass eine serverseitig gerenderte Seite über dieselbe TCP-Verbindung gestreamt wird, die das HTML liefert. Das ist das Feature, zu dem ich die meisten Fragen bekomme, daher ist das Pattern es wert, einmal gezeigt zu werden:
// src/routes/blog/$slug.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/blog/$slug")({
loader: async ({ params, context }) => {
const post = await context.db
.selectFrom("posts")
.selectAll()
.where("slug", "=", params.slug)
.executeTakeFirst();
if (!post) throw notFound();
return { post };
},
component: BlogPost,
});
function BlogPost() {
const { post } = Route.useLoaderData();
return (
<article className="prose mx-auto">
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.bodyHtml }} />
</article>
);
}
Der $slug-Parameter ist typisiert. Der Return-Wert des Loaders ist typisiert. Der useLoaderData()-Hook ist typisiert. Wenn ich ein Feld im Schema umbenenne, fängt der Compiler jeden Consumer im selben pnpm tsc-Durchlauf ab. Die offiziellen TanStack Router Docs erklären die vollständige Type-Safety-Story.
TanStack Query für den Client-seitigen Cache
Für alles, was keine Route ist – Modal-Daten, Dashboards, Dropdowns, unendliches Scrollen – wird der Boilerplate mit TanStack Query an der Loader-Grenze geliefert. Das Pattern, das ich in jedem Projekt durchsetze: Route-Loader hydrieren die initiale Seiten-Payload, und TanStack Query verwaltet den interaktiven Status nach der Hydration. Das ist dieselbe Aufteilung, die Remix propagiert hat, und es bleibt die richtige. Der Boilerplate wird mit Query-Keys geliefert, die automatisch aus dem Route-Tree abgeleitet werden, mit Stale-Time-Defaults, die für SaaS optimiert sind, und einer retry-Policy, die keine 4xx-Fehler wiederholt.
TanStack Form + Zod für jedes Formular
Formulare sind das, wo Solo-Devs die meiste Zeit verlieren. Der Boilerplate wird mit TanStack Form geliefert, gebunden an Zod-Schemas, mit einer Field-Error-Komponente, einem Optimistic-Submission-Pattern und einem <Form>-Wrapper, der die URL-State-Übergabe handhabt. Multi-Step-Wizards, Server Actions und Field-Arrays sind alle in derselben Komponenten-API. Ich habe seit dem Einbau dieses Systems kein neues Form-Scaffold mehr von Grund auf schreiben müssen.
Die langweiligen, aber entscheidenden Features: Auth, Billing, E-Mail
Diese drei sind die Features, ohne die ich kein bezahltes Produkt ausliefern kann. Jedes davon hat den besseren Teil eines Jahres gebraucht, um es richtig hinzubekommen, und jedes hat mich Produktionsstunden gekostet, als ich Abstriche gemacht habe.
Better-Auth mit Edge-Sessions
Der Boilerplate wird mit Better-Auth geliefert, verdrahtet mit Cloudflare D1 mit Edge-residenten Sessions. Der Grund, warum diese Kombination wichtig ist: Sessions werden an demselben Edge-Knoten validiert, der die Anfrage bedient, was bedeutet, dass der Auth-Check in einstelligen Millisekunden gemessen wird, statt in einem Round-Trip zur primären Datenbank. Ich habe persönlich eine Session-Table-Hot-Partition debuggt, die den gesamten Anmelde-Flow auf Postgres serialisiert hat; auf D1 war dieselbe Workload kein Problem. Die Session-Tabelle ist nach user_id partitioniert, mit einem rotierenden HMAC signiert und wird bei jeder Privilegien-Eskalation rotiert. Wenn Sie gegen das Lucia v3-Sunset evaluieren, ist Better-Auth der Weg, den ich jetzt einschlagen würde.
Stripe Billing mit idempotenten Webhooks
Das Billing-Modul ist der Teil von TanStack Ship, auf den ich am stolzesten bin, weil es auch der Teil ist, der mich am meisten gebissen hat. Stripe-Webhooks werden mindestens einmal zugestellt, und die einzige korrekte Antwort ist Idempotenz, geschlüsselt nach event.id. Der Boilerplate wird mit diesem Pattern geliefert, verdrahtet, mit dem Webhook-Handler, der das Event in D1 inside demselben Transaction wie die Abonnement-Zustandsänderung committed:
// src/routes/api/stripe/webhook.ts
import Stripe from "stripe";
import { eq } from "drizzle-orm";
export async function handleStripeWebhook(
req: Request,
env: Env,
ctx: ExecutionContext,
) {
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
const sig = req.headers.get("stripe-signature")!;
const body = await req.text();
const event = stripe.webhooks.constructEvent(body, sig, env.STRIPE_WEBHOOK_SECRET);
await env.DB.batch([
env.DB.insert(events).values({
id: event.id,
type: event.type,
payload: JSON.stringify(event),
receivedAt: new Date(),
}).onConflictDoNothing(),
syncSubscriptionState(env.DB, event),
]);
ctx.waitUntil(announceBillingEvent(env, event));
return new Response("ok");
}
Das zwei-Statement batch() ist das tragende Detail. events.id hat einen eindeutigen Index; wenn Stripe denselben Webhook erneut zustellt, ist das zweite Insert ein No-Op, und der Abonnement-Zustand synchronisiert trotzdem einmal. Die vollständige Aufschlüsselung, wie dies mit D1-Write-Contention umgeht, finden Sie in meinem D1 Write-Lock Postmortem und das Live-Modul ist dokumentiert auf /features. Die offiziellen Stripe Webhook Idempotency Docs sind die Referenz, der ich für den Vertrag vertraue.
Transaktionale E-Mail über Resend + Queues
E-Mail ist die dritte Säule. Der Boilerplate wird mit Resend geliefert, verdrahtet durch einen Cloudflare Queue-Consumer, mit einem per-Tenant Rate-Limit, einer Dead-Letter-Queue nach drei Retries und einem React Email-basierten Template-System. Direkt vom Request-Handler zu senden ist ein Eigentor: Ein Resend-API-Hickup wird Sie alarmieren, wenn die Zahlung des Kunden fehlgeschlagen ist, nicht wenn die E-Mail fehlschlug. Die Queue zwischen den Request und den API-Call zu setzen bedeutet, dass der Benutzer einen 200er sieht, selbst wenn Resend zehn Minuten lang down ist, und die Retry-Schleife später aufholt.
Edge-native Features, die die Form der App verändern
Dies sind die Features, die nur auf Cloudflares Edge Sinn ergeben. Keines davon ist einzigartig für TanStack Ship – die Cloudflare Workers Platform unterstützt sie alle – aber der Boilerplate verdrahtet sie auf eine Weise, die schwer zu kopieren ist.
D1 mit Drizzle ORM
Die Datenschicht ist Cloudflare D1, gelesen durch Drizzle ORM. Drizzle gibt Ihnen die Type-Safety von Prisma ohne den Runtime-Overhead, was wichtig ist, wenn jede Millisekunde CPU abgerechnet wird. Der Boilerplate wird mit einem Migrations-Workflow, einem selectFrom-Query-Builder und einer Drizzle-Extension für Soft-Delete geliefert. Der Single-Writer-Vorbehalt, den ich in der Produktion erreicht habe, ist in meinem D1 Production Guide dokumentiert; die Kurzversion: Halten Sie heiße Tabellen unter 10.000 Writes/Minute pro Datenbank und Sie werden nie Write-Contention sehen.
R2 mit null Egress für Benutzer-Uploads
Benutzer-Uploads – Profilbilder, Exporte, generierte PDFs – gehen nach Cloudflare R2 mit null Egress-Gebühren. Der Boilerplate wird mit einem Presigned-URL-Pattern, einem per-Tenant-Prefix und einer CDN-Cache-Schicht vor R2 geliefert, sodass dasselbe Bild nicht über Benutzer hinweg neu abgerufen wird. Ein rauer Rand: R2s Listing-API cappt bei 1.000 Keys pro Call, was mich zweimal bei Verzeichnis-Style-Features gebissen hat. Die Lösung ist, Ihren eigenen Index in D1 zu pflegen; der Boilerplate wird mit diesem Helper geliefert.
Durable Objects für Echtzeit und Rate-Limits
Echtzeit-Features – Chat, Presence, kollaboratives Editieren – laufen auf Cloudflare Durable Objects. Der Boilerplate wird mit einem Room Durable Object mit WebSocket-Hibernation, einem per-User Rate-Limit DO und einem CRDT-Lite-Pattern für geteilte Cursors geliefert. Ich habe Chat, Live-Dashboards und kollaborative Formulare auf dieser Primitive ausgeliefert. Die ehrliche Grenze: Durable Objects partitionieren nach ID, sodass eine einzelne heiße ID den gesamten Traffic für diese ID serialisiert. Die Lösung ist, nach Hash zu sharden für sehr heiße Keys; der Boilerplate wird mit einem ShardedCounter-Helper für genau diesen Fall geliefert.
Workers AI für Embeddings und Moderation
Für alles, was Embeddings, Klassifizierung oder kleine generative Aufgaben braucht, integriert der Boilerplate Cloudflare Workers AI mit einem Model-Router, der über @cf/meta/llama-3.1-8b-instruct, @cf/baai/bge-base-en-v1.5 und @cf/microsoft/resnet-50 fällt. Für alles Schwerere als ~3k Tokens sollten Sie einen dedizierten Provider aufrufen – Workers AI ist kein Frontier-Model-Ersatz. Ich nutze es für Embeddings, Intent-Klassifizierung und Bildmoderation; für Chat nutze ich es nicht.
DX-Features, die leicht zu unterschätzen sind
Die letzte Kategorie ist die, die bestimmt, ob Sie den Boilerplate tatsächlich ausliefern oder nach einem Wochenende aufgeben.
Typsicheres Environment mit t3-env
Der Boilerplate wird mit t3-env geliefert, sodass jedes process.env.STRIPE_SECRET_KEY zu einem typisierten env.STRIPE_SECRET_KEY wird mit einem Build-Time-Failure, wenn das Secret fehlt. Das ist ein Ein-Zeilen-Produktivitätsgewinn, der eine Klasse von Ausfällen um 2 Uhr nachts verhindert, bei denen ein Tippfehler in einem Env-Var-Namen still zu undefined zur Runtime wurde.
Observability-Hooks, die alarmieren, bevor es Ihre Kunden tun
Der Boilerplate wird mit Cloudflare Workers Logs geliefert, standardmäßig aktiviert, einer Logpush-Konfiguration für Produktion und einem Structured-Logging-Helper, der JSON mit einer Request-ID, User-ID und Trace-Kontext emittiert. Das einzelne nützlichste Debugging-Tool ist die Request-ID, die es Ihnen ermöglicht, den Pfad eines einzelnen Benutzers durch das System in Sekunden zu greppen. Ich habe dies noch nicht durch einen Vendor ersetzt; für die Solo-Dev-Skala reicht es aus.
CLI für die langweiligen Teile
Die ship CLI übernimmt die langweiligen Teile: Projekt-Init, D1-Migrations-Runner, Secret-Rotation, Deployment und Tail-Logs. Nichts davon ist neuartig, aber es vorkonfiguriert zu haben bedeutet, dass die erste Stunde eines neuen Projekts nicht für das Zusammenbauen der Toolchain draufgeht.
Ehrliche Grenzen – was 2026 noch holprig ist
Das ist der Teil, den die meisten Boilerplate-Seiten überspringen, und es ist der Teil, den ich klar benennen werde.
Langlebige Hintergrundjobs
Cloudflare Workers CPU-Billing toppt bei 30 Sekunden Wall-Time pro Request, und Cloudflare Queue-Consumer timen bei 15 Minuten aus. Für alles Schwerere als ein Fan-Out-Webhook werden Sie an eine Wand stoßen. Die Antwort des Boilerplates ist, Queues zu verketten und Durable Object Alarms für zustandsbehaftete Retries zu nutzen, aber wenn Ihr Anwendungsfall ein 10-minütiges Video verarbeiten oder ein 50 MB großes PDF generieren muss, schauen Sie sich die falsche Platform an.
Multi-Region Durable Object-Koordination
Durable Objects sind von Design her Single-Region. Wenn Sie einen koordinierten Zähler oder einen globalen Chatroom brauchen, wählen Sie eine Region und zahlen die Latenz-Steuer von der anderen Seite des Planeten. Cloudflares DOs haben einen Smart-Placement-Modus, der hilft, aber er ist nicht kostenlos und nicht transparent. Ich habe Smart-Placement nicht persönlich bei 1 Million concurrenter User benchmarkt; wenn Sie in dieser Größenordnung sind, würde ich Sie zu einer dedizierten Echtzeit-Plattform schieben.
shadcn/ui-Komponenten-Drift
Der Boilerplate wird mit shadcn/ui-Komponenten geliefert, aber Komponenten driften. Ein Dialog von Januar 2026 sieht nicht aus wie ein Dialog von August 2026, und Updates sind nicht immer abwärtskompatibel. Der Boilerplate pinnt einen Snapshot; wenn Sie shadcn add einer neuen Komponente oben drauf nutzen, erben Sie den Upstream-Drift-Zeitplan. Die ehrliche Lösung ist, die Komponenten zu forken, von denen Sie abhängen, was die meisten Teams sowieso irgendwann tun.
TanStack Start ist noch 1.x
TanStack Start hat seine 1.0 2025 ausgeliefert und bewegt sich seitdem schnell. Der Boilerplate pinnt eine Version, aber jedes Minor-Upgrade hat mindestens einen Route-Loader in meinen Projekten angefasst. Planen Sie einen halben Tag pro Minor-Upgrade, wenn Sie eine große App haben.
Abschließende CTA
Wenn Sie TanStack Ship für ein echtes Produkt evaluieren, beantwortet die Preis- und Lizenzierungsseite die Budgetfrage und die Features-Seite ist das kanonische Inventar. Für einen Direktvergleich gegen ShipFast, Makerkit, Supastarter oder einen der anderen Starter finden Sie den Vergleichs-Hub und den Alternativen-Hub mit alles Nebeneinander. Wenn Sie das vollständige Postmortem hinter dem Billing-Modul wollen, das D1-Write-Contention handhabt, finden Sie es im Blog-Index – derselbe Ort, an dem dieser Beitrag lebt, sobald Sie ihn fertig gelesen haben.