GameCraftGameCraft

Project Structure

Understanding ProductReady's architecture - where everything lives and why

Project Structure

ProductReady follows a clear, logical structure. This guide helps you understand where everything lives and where to add your code.

New to Next.js? This structure follows Next.js 15+ App Router conventions with additional organization for scalability.


Overview

apps/productready/
├── src/                    # All application code
│   ├── app/                # Next.js pages & routes
│   ├── components/         # React components
│   ├── db/                 # Database schema & queries
│   ├── lib/                # Utilities & configuration
│   └── server/             # Server-side code (tRPC)
├── content/                # Documentation (MDX)
├── public/                 # Static assets
├── spec/                   # Product specs & design docs
└── tests/                  # Test files

Detailed Structure

apps/productready
├─ src
│  ├─ app
│  │  ├─ (home)/
│  │  ├─ dashboard/
│  │  ├─ api/health/route.ts
│  │  ├─ docs/[[...slug]]/page.tsx
│  │  ├─ layout.tsx
│  │  └─ global.css
│  ├─ components
│  │  ├─ ui/ (button.tsx, card.tsx, ...)
│  │  └─ auth/auth-button.tsx
│  ├─ db
│  │  ├─ schema/ (users.ts, tasks.ts, posts.ts)
│  │  ├─ index.ts
│  │  └─ seed.ts
│  ├─ lib
│  │  ├─ trpc/client.ts
│  │  ├─ auth.ts
│  │  └─ demo.ts
│  └─ server
│     ├─ routers/ (tasks.ts, users.ts)
│     ├─ trpc.ts
│     └─ index.ts
├─ content
│  └─ docs
│     ├─ en/ (index.mdx, quick-start.mdx, ...)
│     └─ zh-CN/ (index.mdx, ...)
├─ public/assets/
├─ spec/ (product-design.md, icp-guide.md, ...)
├─ .env.example
├─ package.json
├─ next.config.mjs
└─ drizzle.config.ts

Key Directories Explained

src/app/ - Next.js Pages

This is where your pages and routes live. Next.js uses file-based routing.

Folder structure = URL structure:

  • app/(home)/page.tsx/ (landing page)
  • app/dashboard/page.tsx/dashboard
  • app/dashboard/tasks/page.tsx/dashboard/tasks
  • app/api/health/route.ts/api/health (API endpoint)

Route Groups: (home) - Parentheses don't affect URL, just for organization

Special files:

  • page.tsx - The actual page component
  • layout.tsx - Shared layout wrapper
  • loading.tsx - Loading state
  • error.tsx - Error boundary
  • route.ts - API endpoint

Where to add:

  • New pages → Create folder with page.tsx
  • API endpoints → Create route.ts in app/api/

SPA Routing with Wouter

ProductReady uses Wouter for client-side SPA (Single Page Application) routing within dashboard pages.

Architecture Pattern:

  • Next.js App Router handles top-level routes with SSR (/dashboard, /agent, /kadmin)
  • Wouter handles sub-routes within each page for SPA navigation without full page reloads

Example (app/dashboard/page.tsx):

import { Route, Switch } from 'wouter';
import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar';
import { AppSidebar } from '@/components/app-sidebar';

export default function DashboardPage() {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        {/* Wouter routes for SPA navigation */}
        <Switch>
          <Route path="/analytics">
            <AnalyticsView />
          </Route>
          <Route path="/reports">
            <ReportsView />
          </Route>
          <Route path="/">
            <DashboardOverview />
          </Route>
        </Switch>
      </SidebarInset>
    </SidebarProvider>
  );
}

Benefits:

  • Fast navigation - No full page reloads between sub-routes
  • Better UX - Smooth transitions, preserved sidebar state
  • SEO-friendly - Next.js handles initial SSR, Wouter for client-side
  • Independent sections - Each dashboard page has its own wouter instance

When to use:

  • ✅ Dashboard sections with multiple views (/dashboard, /agent, /kadmin)
  • ✅ Any page with tabs/navigation that doesn't need SSR per sub-route
  • ❌ Public pages that need per-route SEO (use Next.js App Router instead)

src/components/ - React Components

Reusable UI components organized by category.

Organization:

components/
├── ui/              # Generic UI components (Button, Card, Dialog)
├── auth/            # Auth-specific (LoginForm, AuthButton)
├── dashboard/       # Dashboard-specific components
└── shared/          # Truly shared across all pages

When to create a component:

  • ✅ Used in 2+ places
  • ✅ Complex logic that clutters page
  • ✅ Reusable pattern (forms, cards, modals)

When NOT to:

  • ❌ One-off UI (just put in page.tsx)
  • ❌ Too granular (don't extract every <div>)

KUI Library: Don't modify packages/kui directly! Create wrapper components in src/components/ui/ instead.


src/db/ - Database Layer

Everything database-related: schemas, migrations, queries.

Key files:

  • schema/*.ts - Table definitions (Drizzle ORM)
  • index.ts - Database client export
  • seed.ts - Sample data for development

Example schema (schema/tasks.ts):

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

export const tasks = pgTable('tasks', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  description: text('description'),
  status: text('status', { 
    enum: ['pending', 'in_progress', 'completed'] 
  }).default('pending'),
  createdAt: timestamp('created_at').defaultNow(),
  updatedAt: timestamp('updated_at').defaultNow(),
});

Where to add:

  • New table → Create schema/your-table.ts
  • Queries → Add to tRPC router (see /server/routers/)

src/server/ - Server-Side Code

Backend logic: tRPC routers, procedures, middleware.

Structure:

server/
├── routers/          # tRPC routers (one per domain)
│   ├── tasks.ts      # tasks.list, tasks.create, etc.
│   ├── users.ts      # users.me, users.update, etc.
│   └── index.ts      # Combines all routers
├── trpc.ts           # tRPC config & procedures
└── index.ts          # Main tRPC app export

Example router (routers/tasks.ts):

import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '../trpc';

export const tasksRouter = createTRPCRouter({
  list: protectedProcedure
    .input(z.object({ limit: z.number().default(20) }))
    .query(async ({ ctx, input }) => {
      return ctx.db.select().from(tasks).limit(input.limit);
    }),
    
  create: protectedProcedure
    .input(z.object({ title: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return ctx.db.insert(tasks).values(input);
    }),
});

Where to add:

  • New API routes → Create router in routers/
  • Shared logic → Add to trpc.ts (middleware, context)

src/lib/ - Utilities & Configuration

Helpers, configs, and third-party integrations.

Common files:

  • auth.ts - Better Auth configuration
  • trpc/client.ts - tRPC client setup
  • demo.ts - Demo mode utilities
  • utils.ts - Shared helper functions

What goes here:

  • Configuration objects
  • Helper functions
  • Third-party SDK wrappers
  • Constants and enums

content/docs/ - Documentation

MDX files for documentation (this page you're reading!).

Structure:

content/docs/
├── en/               # English docs
│   ├── index.mdx
│   ├── quick-start.mdx
│   └── features/
└── zh-CN/            # Chinese docs
    └── index.mdx

Powered by Fumadocs - automatically generates:

  • Sidebar navigation
  • Search functionality
  • Table of contents
  • Syntax highlighting

public/ - Static Assets

Files served directly at root URL.

What goes here:

  • public/logo.pnghttps://yoursite.com/logo.png
  • public/assets/icons/ → Static icons
  • public/robots.txt → SEO files
  • public/favicon.ico → Site favicon

What NOT to put:

  • ❌ Large images (use CDN instead)
  • ❌ User uploads (use S3/Cloudinary)
  • ❌ Sensitive files

spec/ - Product Specifications

Internal docs for product, design, and engineering.

Files:

  • product-design.md - PRD (product requirements)
  • icp-guide.md - Ideal customer profiles
  • marketing-guide.md - Messaging and positioning
  • design-system.md - Design tokens and patterns

For: Internal team alignment, onboarding, decisions

Not for: User-facing documentation (that's in content/docs/)


Common Patterns

Adding a New Feature

Example: Add a "Comments" feature

  1. Database schemasrc/db/schema/comments.ts

    export const comments = pgTable('comments', {
      id: serial('id').primaryKey(),
      taskId: integer('task_id').references(() => tasks.id),
      content: text('content').notNull(),
      authorId: text('author_id').notNull(),
      createdAt: timestamp('created_at').defaultNow(),
    });
  2. tRPC routersrc/server/routers/comments.ts

    export const commentsRouter = createTRPCRouter({
      list: protectedProcedure.query(...),
      create: protectedProcedure.mutation(...),
    });
  3. UI componentsrc/components/comments/comment-list.tsx

    export function CommentList({ taskId }: { taskId: number }) {
      const { data } = trpc.comments.list.useQuery({ taskId });
      // ... render comments
    }
  4. Page → Use component in src/app/dashboard/tasks/[id]/page.tsx

    <CommentList taskId={task.id} />
  5. Migrations → Run pnpm db:generate and pnpm db:migrate


Adding a New Page

Example: Add /dashboard/analytics

  1. Create folder: src/app/dashboard/analytics/

  2. Add page: src/app/dashboard/analytics/page.tsx

    export default function AnalyticsPage() {
      return (
        <div>
          <h1>Analytics</h1>
          {/* Your content */}
        </div>
      );
    }
  3. (Optional) Add layout: layout.tsx if you need custom wrapper

  4. Visit: http://localhost:3000/dashboard/analytics


Adding an API Endpoint

Example: Add /api/webhook

  1. Create: src/app/api/webhook/route.ts

    import { NextResponse } from 'next/server';
    
    export async function POST(request: Request) {
      const body = await request.json();
      // Handle webhook
      return NextResponse.json({ success: true });
    }
  2. Access: POST https://yoursite.com/api/webhook


File Naming Conventions

Pages & Routes:

  • page.tsx - Page component
  • layout.tsx - Layout wrapper
  • route.ts - API endpoint
  • [id] - Dynamic route (e.g., /tasks/[id])
  • [[...slug]] - Catch-all route (docs)

Components:

  • kebab-case.tsx - Component files (user-profile.tsx)
  • PascalCase - Component names (UserProfile)

Utilities:

  • camelCase.ts - Utility files (formatDate.ts)
  • camelCase - Function names (formatDate())

Database:

  • snake_case - Table/column names (created_at)
  • camelCase - TypeScript variables (createdAt)

Configuration Files

package.json

Dependencies, scripts, and metadata.

Key scripts:

{
  "dev": "next dev --port 3000",
  "build": "next build",
  "db:migrate": "drizzle-kit migrate"
}

next.config.mjs

Next.js configuration (rewrites, headers, env).

drizzle.config.ts

Database connection and migration settings.

tsconfig.json

TypeScript compiler options.

Path aliases:

{
  "paths": {
    "~/*": ["./src/*"]
  }
}

Lets you import like: import { db } from '~/db'


Best Practices

✅ Do

  • Keep page components small (extract to /components)
  • One router per domain (tasksRouter, usersRouter)
  • Co-locate related files (feature folders)
  • Use path aliases (~/* instead of ../../..)
  • Follow existing patterns in codebase

❌ Don't

  • Put business logic in page components
  • Create huge "god" routers with 20+ procedures
  • Mix database queries with UI code
  • Duplicate code (extract to utilities)
  • Modify packages/kui directly

Next Steps

Now that you understand the structure:

Questions? Check the Troubleshooting guide.

On this page