scaffold-route
Scaffold Route
Create a complete vertical slice for a new feature: page, API, and validation.
What gets created
app/[feature]/
├── page.tsx # Server component
├── loading.tsx # Skeleton loader
├── error.tsx # Error boundary
└── components/
└── index.ts # Barrel export
server/api/routers/[feature]/
└── index.ts # tRPC router with CRUD procedures
validations/
└── [feature].ts # Zod schemas + inferred types
What gets updated
server/api/root.ts- import and add router to appRoutervalidations/index.ts- add barrel export
Instructions
- Ask for the feature name (singular, lowercase, e.g., "project", "booking", "invoice")
- Ask which CRUD operations are needed: list, get, create, update, delete
- Generate files following the patterns below
- Update barrel exports and root router
Patterns
page.tsx (Server Component)
import { Suspense } from 'react'
import { FeatureList } from './components'
import { FeatureSkeleton } from '@/components/skeletons'
export default function FeaturePage() {
return (
<main className="container py-8">
<h1 className="text-2xl font-bold mb-6">Features</h1>
<Suspense fallback={<FeatureSkeleton />}>
<FeatureList />
</Suspense>
</main>
)
}
loading.tsx
import { FeatureSkeleton } from '@/components/skeletons'
export default function Loading() {
return <FeatureSkeleton />
}
error.tsx
'use client'
interface Props {
error: Error & { digest?: string }
reset: () => void
}
export default function Error({ error, reset }: Props) {
return (
<main className="container py-8">
<h1 className="text-2xl font-bold mb-4">Something went wrong</h1>
<p className="text-muted-foreground mb-4">{error.message}</p>
<button onClick={reset} className="text-primary underline">
Try again
</button>
</main>
)
}
validations/[feature].ts
import { z } from 'zod'
export const createFeatureSchema = z.object({
name: z.string().min(1, 'Name is required'),
})
export const updateFeatureSchema = createFeatureSchema.partial()
export type CreateFeatureInput = z.infer<typeof createFeatureSchema>
export type UpdateFeatureInput = z.infer<typeof updateFeatureSchema>
server/api/routers/[feature]/index.ts
import { z } from 'zod'
import { router, publicProcedure } from '@/server/api/trpc'
import { createFeatureSchema, updateFeatureSchema } from '@/validations'
import { prisma } from '@/prisma/prisma'
export const featureRouter = router({
list: publicProcedure.query(async () => {
return prisma.feature.findMany({
select: { id: true, name: true, createdAt: true },
orderBy: { createdAt: 'desc' },
})
}),
get: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return prisma.feature.findUniqueOrThrow({
where: { id: input.id },
select: { id: true, name: true, createdAt: true },
})
}),
create: publicProcedure
.input(createFeatureSchema)
.mutation(async ({ input }) => {
return prisma.feature.create({
data: input,
select: { id: true },
})
}),
update: publicProcedure
.input(z.object({ id: z.string(), data: updateFeatureSchema }))
.mutation(async ({ input }) => {
return prisma.feature.update({
where: { id: input.id },
data: input.data,
select: { id: true },
})
}),
delete: publicProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ input }) => {
return prisma.feature.delete({
where: { id: input.id },
select: { id: true },
})
}),
})
Updating root.ts
import { featureRouter } from './routers/feature'
export const appRouter = router({
// existing routers...
feature: featureRouter,
})
Note: Prisma model scaffolding is currently under review. For now, create models manually in
prisma/schema.prismaand run migrations before using this skill.
Checklist
- Feature name is singular and lowercase
- All files use
@/imports - Barrel exports updated
- Root router updated
- Skeleton component exists or created
- No
anytypes - No semicolons
More from cerico/macfair
infographic
Generate infographics from text. Extracts key info, renders SVG, exports PNG. Uses Claude Code (no API costs).
38visx
Build data visualizations with visx (React + D3). Use for charts, graphs, and interactive data exploration.
21creative-design
Create distinctive, memorable UI for landing pages, portfolios, marketing sites, and one-off creative work. Use when the user explicitly wants something "distinctive", "creative", "memorable", or "unique" - NOT for standard app components where consistency matters.
20threejs
Build 3D scenes, animations, and interactive experiences with Three.js. Use for product viewers, backgrounds, data visualization, or creative experiments.
13prototype
Create quick React prototypes that bundle to a single HTML file. Use for demos, interactive experiments, or shareable artifacts - NOT for full apps (use audreygen/Next.js for those).
13test-review
Review existing tests for completeness, quality issues, and common mistakes
12