Skip to main content

Kirimase vs Create T3 App: CLI-Generated Starters 2026

·StarterPick Team
kirimaset3-stackcreate-t3-appcliscaffoldtypescript2026

TL;DR

They're complementary, not competitors — and the community knows it. create-t3-app generates your initial Next.js + tRPC + Prisma project scaffold once. Kirimase adds ongoing code generation: run kirimase generate to scaffold Prisma models, tRPC routers, Zod validators, and UI forms throughout development. Think of create-t3-app as your project initializer and Kirimase as your development accelerator. Many teams use both: T3 to start, Kirimase to maintain velocity.

Key Takeaways

  • create-t3-app: ~270K downloads/week, one-time scaffold, the gold standard initial setup
  • Kirimase: smaller but growing, ongoing CRUD generation on top of T3 projects
  • Kirimase init: supports T3 Stack, Next.js + Drizzle, custom configs
  • Kirimase generate: creates Prisma model → tRPC router → Zod schema → UI form in one command
  • DX: Both are CLI-based, Kirimase is more opinionated about the generated patterns
  • Use together: start with create-t3-app, add Kirimase later for feature scaffolding

Create T3 App: The Standard Initializer

npm create t3-app@latest
# Interactive prompts:
#   ✔ What will your project be called? my-saas
#   ✔ Will you be using TypeScript? Yes
#   ✔ Will you be using tRPC? Yes
#   ✔ What authentication provider would you like to use? NextAuth.js
#   ✔ What database ORM would you like to use? Prisma
#   ✔ Would you like to use Tailwind CSS? Yes
#   ✔ What package manager? pnpm
#   ✔ Should we run 'pnpm install' for you? Yes
#   ✔ Initialize a new git repository? Yes

What You Get

Generated structure:
my-saas/
  ├── src/
  │   ├── app/
  │   │   ├── layout.tsx
  │   │   ├── page.tsx
  │   │   └── api/
  │   │       ├── auth/[...nextauth]/route.ts
  │   │       └── trpc/[trpc]/route.ts
  │   ├── server/
  │   │   ├── api/
  │   │   │   ├── root.ts         ← tRPC root router
  │   │   │   ├── trpc.ts         ← tRPC context setup
  │   │   │   └── routers/
  │   │   │       └── post.ts     ← Example router
  │   │   ├── auth.ts             ← NextAuth config
  │   │   └── db.ts               ← Prisma client
  │   ├── trpc/
  │   │   ├── query-client.ts     ← TanStack Query config
  │   │   ├── react.tsx           ← Client-side hooks
  │   │   └── server.ts           ← Server-side caller
  │   └── styles/globals.css
  ├── prisma/schema.prisma
  ├── .env.example
  └── next.config.js

T3's generated code is minimal and clean — it gives you the wiring, not the features.

// Generated tRPC router (src/server/api/routers/post.ts):
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '~/server/api/trpc';

export const postRouter = createTRPCRouter({
  hello: publicProcedure
    .input(z.object({ text: z.string() }))
    .query(({ input }) => ({ greeting: `Hello ${input.text}` })),

  create: protectedProcedure
    .input(z.object({ name: z.string().min(1) }))
    .mutation(async ({ ctx, input }) => {
      return ctx.db.post.create({
        data: { name: input.name, createdBy: { connect: { id: ctx.session.user.id } } },
      });
    }),

  getLatest: protectedProcedure.query(async ({ ctx }) => {
    return ctx.db.post.findFirst({
      orderBy: { createdAt: 'desc' },
      where: { createdBy: { id: ctx.session.user.id } },
    });
  }),
});

Kirimase: Ongoing Code Generation

Kirimase wraps the T3 stack and adds kirimase generate — the ability to scaffold new models with full CRUD stack throughout development.

# Install globally:
npm install -g kirimase

# Initialize in an existing Next.js project (or start fresh):
kirimase init
# Prompts: Database? Prisma | ORM? Yes | Auth? NextAuth | Package manager? pnpm

# Add a package with guided configuration:
kirimase add
# Prompts: What to add? (shadcn, stripe, resend, clerk, lucia...)

# Generate full CRUD for a model:
kirimase generate

kirimase generate In Action

kirimase generate
# Prompts:
#   ✔ Model name: Project
#   ✔ Add field: name | String | required
#   ✔ Add field: description | String | optional
#   ✔ Add field: status | Enum (active, paused, archived) | default: active
#   ✔ Add field: userId | String | foreignKey → User
#   ✔ Done adding fields
#   ✔ Generate server actions (not tRPC)? No, use tRPC

What gets generated:

// 1. Prisma schema addition (appended to schema.prisma):
model Project {
  id          String   @id @default(cuid())
  name        String
  description String?
  status      ProjectStatus @default(active)
  userId      String
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

enum ProjectStatus {
  active
  paused
  archived
}
// 2. Zod schema (src/lib/db/schema/projects.ts):
import { z } from 'zod';

export const projectIdSchema = z.object({ id: z.string() });
export type ProjectId = z.infer<typeof projectIdSchema>;

export const insertProjectSchema = z.object({
  name: z.string(),
  description: z.string().optional(),
  status: z.enum(['active', 'paused', 'archived']).default('active'),
});
export type NewProjectParams = z.infer<typeof insertProjectSchema>;

export const updateProjectSchema = insertProjectSchema;
export type UpdateProjectParams = z.infer<typeof updateProjectSchema>;
// 3. tRPC router (src/server/api/routers/projects.ts):
import { createTRPCRouter, protectedProcedure } from '~/server/api/trpc';
import {
  projectIdSchema,
  insertProjectSchema,
  updateProjectSchema,
} from '~/lib/db/schema/projects';

export const projectsRouter = createTRPCRouter({
  getProjects: protectedProcedure.query(async ({ ctx }) => {
    return ctx.db.project.findMany({ where: { userId: ctx.session.user.id } });
  }),

  getProjectById: protectedProcedure
    .input(projectIdSchema)
    .query(async ({ ctx, input }) => {
      return ctx.db.project.findFirst({
        where: { id: input.id, userId: ctx.session.user.id },
      });
    }),

  createProject: protectedProcedure
    .input(insertProjectSchema)
    .mutation(async ({ ctx, input }) => {
      return ctx.db.project.create({
        data: { ...input, userId: ctx.session.user.id },
      });
    }),

  updateProject: protectedProcedure
    .input(updateProjectSchema.extend({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input;
      return ctx.db.project.update({ where: { id }, data });
    }),

  deleteProject: protectedProcedure
    .input(projectIdSchema)
    .mutation(async ({ ctx, input }) => {
      return ctx.db.project.delete({ where: { id: input.id } });
    }),
});
// 4. React form component (src/components/projects/ProjectForm.tsx):
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { api } from '~/trpc/react';
import { insertProjectSchema, type NewProjectParams } from '~/lib/db/schema/projects';

export function ProjectForm({ onSuccess }: { onSuccess?: () => void }) {
  const { register, handleSubmit, formState: { errors } } = useForm<NewProjectParams>({
    resolver: zodResolver(insertProjectSchema),
  });

  const createProject = api.projects.createProject.useMutation({
    onSuccess: () => {
      onSuccess?.();
    },
  });

  return (
    <form onSubmit={handleSubmit((data) => createProject.mutate(data))}>
      <div>
        <label>Name</label>
        <input {...register('name')} />
        {errors.name && <p>{errors.name.message}</p>}
      </div>
      <div>
        <label>Description</label>
        <textarea {...register('description')} />
      </div>
      <div>
        <label>Status</label>
        <select {...register('status')}>
          <option value="active">Active</option>
          <option value="paused">Paused</option>
          <option value="archived">Archived</option>
        </select>
      </div>
      <button type="submit" disabled={createProject.isPending}>
        {createProject.isPending ? 'Creating...' : 'Create Project'}
      </button>
    </form>
  );
}

One kirimase generate command = Prisma model + Zod schemas + tRPC CRUD router + React form.


Comparison

create-t3-appKirimase
PurposeInitial project scaffoldOngoing feature generation
When to useProject creationThroughout development
Generated outputFull project structurePer-model CRUD stack
FlexibilityHigh (minimal opinions)Medium (opinionated patterns)
Downloads/week~270KSmaller
Auth supportNextAuthNextAuth, Lucia, Clerk
ORM supportPrisma, DrizzlePrisma, Drizzle
MaintenanceT3 OSS teamCommunity

Decision Guide

Use create-t3-app if:
  → Starting a new T3-stack project (always use this)
  → Want the community-standard setup
  → Prefer minimal generated code you write yourself

Use Kirimase if:
  → Already have a T3/Next.js project
  → Tired of writing boilerplate CRUD manually
  → Want consistent patterns across all models
  → Building data-heavy apps (lots of models)

Use both (recommended):
  1. create-t3-app to initialize
  2. Kirimase for each new model/resource
  → Get T3's quality foundation + Kirimase's velocity

Explore T3 Stack, Kirimase, and all CLI-generated starters at StarterPick.

Comments