Wasp vs T3 Stack vs ShipFast: Full-Stack Framework vs Boilerplate 2026
TL;DR
These aren't comparable — they solve different problems at different price points. Wasp is a full-stack framework with a DSL that generates auth, backend, and deployment config — you write less but accept the framework's model. T3 Stack is a free, widely-understood collection of best-practice tools (Next.js + tRPC + Prisma) that you own completely. ShipFast is a $299 paid template for fast B2C SaaS launch. The right choice depends on what you're optimizing: control (T3), speed with generated code (Wasp), or speed with polished components (ShipFast).
Key Takeaways
- Wasp: free, DSL framework (
.waspfiles), generates React + Node.js, built-in auth/email/jobs/deploy - T3 Stack: free, Next.js + tRPC + Prisma + NextAuth + Tailwind, create-t3-app CLI
- ShipFast: $299 paid template, Next.js + Drizzle + Stripe, battle-tested landing pages
- Control: T3 > ShipFast > Wasp (Wasp abstracts away Node.js layer)
- Time to first feature: Wasp > ShipFast > T3
- Scalability ceiling: T3 > ShipFast > Wasp (Wasp is newer, less proven at scale)
- Community: T3 (largest) > ShipFast > Wasp
Wasp: The Framework Approach
Wasp takes a fundamentally different position: it's a full-stack framework, not a template. You describe your app in a .wasp DSL file, and Wasp generates the entire React + Express.js + database wiring.
// main.wasp — the entire app definition:
app TodoApp {
wasp: { version: "^0.14.0" },
title: "My SaaS App",
auth: {
userEntity: User,
methods: {
email: {
fromField: { name: "My SaaS", email: "hello@mysaas.com" },
emailVerification: { clientRoute: VerifyEmailRoute },
passwordReset: { clientRoute: PasswordResetRoute },
},
google: {},
github: {},
},
onAuthFailedRedirectTo: "/login",
onAuthSucceededRedirectTo: "/dashboard",
},
emailSender: {
provider: Mailgun,
},
}
// Database entity:
entity User {=psl
id Int @id @default(autoincrement())
email String @unique
createdAt DateTime @default(now())
isAdmin Boolean @default(false)
subscription Subscription?
psl=}
entity Subscription {=psl
id Int @id @default(autoincrement())
userId Int @unique
user User @relation(fields: [userId], references: [id])
plan String @default("free")
stripeId String?
psl=}
// Routes and pages:
route DashboardRoute { path: "/dashboard", to: DashboardPage }
page DashboardPage {
component: import { DashboardPage } from "@client/pages/Dashboard",
authRequired: true,
}
// Server action:
action updateSubscription {
fn: import { updateSubscription } from "@server/actions/subscription",
entities: [User, Subscription],
}
// Background job:
job sendWeeklyDigest {
executor: PgBoss,
perform: { fn: import { sendWeeklyDigest } from "@server/jobs/digest" },
schedule: { cron: "0 9 * * 1" }, // Every Monday 9am
}
// Wasp server action (plain TypeScript):
import { type UpdateSubscription } from 'wasp/server/operations';
import { type Subscription } from 'wasp/entities';
export const updateSubscription: UpdateSubscription<
{ plan: string },
Subscription
> = async ({ plan }, context) => {
if (!context.user) throw new Error('Not authenticated');
return context.entities.Subscription.upsert({
where: { userId: context.user.id },
update: { plan },
create: { userId: context.user.id, plan },
});
};
// Wasp client (automatically typed):
import { useQuery, useAction } from 'wasp/client/operations';
import { getSubscription, updateSubscription } from 'wasp/client/operations';
export function DashboardPage() {
const { data: subscription } = useQuery(getSubscription);
const upgrade = useAction(updateSubscription);
return (
<div>
<p>Current plan: {subscription?.plan}</p>
<button onClick={() => upgrade({ plan: 'pro' })}>Upgrade</button>
</div>
);
}
T3 Stack: The Community Standard
npm create t3-app@latest my-app
# Prompts: TypeScript? Yes | tRPC? Yes | Prisma? Yes | NextAuth? Yes | Tailwind? Yes
// T3 tRPC router — full type safety end-to-end:
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '~/server/api/trpc';
export const subscriptionRouter = createTRPCRouter({
get: protectedProcedure.query(async ({ ctx }) => {
return ctx.db.subscription.findFirst({
where: { userId: ctx.session.user.id },
});
}),
update: protectedProcedure
.input(z.object({ plan: z.enum(['free', 'pro', 'team']) }))
.mutation(async ({ ctx, input }) => {
return ctx.db.subscription.upsert({
where: { userId: ctx.session.user.id },
update: { plan: input.plan },
create: { userId: ctx.session.user.id, plan: input.plan },
});
}),
});
// T3 client usage:
import { api } from '~/trpc/react';
export function DashboardPage() {
const { data: subscription } = api.subscription.get.useQuery();
const upgrade = api.subscription.update.useMutation();
return (
<div>
<p>Current plan: {subscription?.plan}</p>
<button onClick={() => upgrade.mutate({ plan: 'pro' })}>Upgrade</button>
</div>
);
}
T3 doesn't include: landing pages, Stripe integration, email templates, admin dashboard. You build these yourself.
ShipFast: The Paid Template
ShipFast takes the "copy-paste into your project" approach rather than framework abstraction. Marc Lou's own product.
// ShipFast: Everything is just Next.js — you see all the code.
// The value is in the pre-built components and patterns.
// Stripe webhook handler (pre-built in ShipFast):
import Stripe from 'stripe';
import { db } from '@/lib/db';
import { users } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get('stripe-signature')!;
const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session;
await db.update(users)
.set({
stripeCustomerId: session.customer as string,
stripeSubscriptionId: session.subscription as string,
stripePriceId: session.metadata?.priceId,
stripeCurrentPeriodEnd: new Date(session.expires_at * 1000),
})
.where(eq(users.email, session.customer_email!));
}
return new Response(null, { status: 200 });
}
ShipFast includes: Landing page sections (Hero, Pricing, FAQ, Testimonials), Stripe + LemonSqueezy, NextAuth, blog, SEO setup, deployment config. No DIY.
Comparison Table
| Wasp | T3 Stack | ShipFast | |
|---|---|---|---|
| Price | Free | Free | $299 |
| Type | Framework | Template/Stack | Paid template |
| API layer | Generated | tRPC | REST/API routes |
| Auth | Built-in (DSL) | NextAuth | NextAuth/Supabase |
| Payments | Manual | Manual | Stripe + LemonSqueezy |
| Landing pages | Manual | Manual | ✅ Built-in |
| Admin | Manual | Manual | ❌ |
| Background jobs | ✅ PgBoss DSL | Manual | Manual |
| Node.js control | Limited | Full | Full |
| Learning curve | High (new DSL) | Medium | Low |
| Community | Growing | Large | Large |
Decision Guide
Choose Wasp if:
→ Want framework-level abstractions (less code to write)
→ Background jobs and cron are core to your app
→ Comfortable learning Wasp's DSL
→ Free is a hard requirement
→ Like opinionated scaffolding
Choose T3 Stack if:
→ Want full control with zero license cost
→ Team knows React/TypeScript well
→ Building a complex product with custom requirements
→ Want the best type-safety (tRPC end-to-end)
→ Fine with building Stripe/auth flows yourself
Choose ShipFast if:
→ Launching a B2C SaaS solo
→ Want landing pages, payments, auth ready-to-go
→ $299 investment justified by saved dev time
→ Targeted at Marc Lou's audience of indie hackers
→ Want LemonSqueezy as payment option
Compare Wasp, T3 Stack, and ShipFast in detail at StarterPick.