Project Structure

The codebase uses Next.js route groups to separate marketing pages from the authenticated app.


Directory overview

app/
  (marketing)/     # Public pages (home, pricing)
  (app)/           # Protected pages (dashboard, billing, settings)
  login/           # Sign-in page
  api/             # API routes (auth, webhooks)

components/
  app-shell.tsx    # App layout with sidebar
  ui/              # shadcn/ui components

db/
  schema.ts        # Drizzle table definitions
  migrations/      # SQL migrations

lib/
  auth.ts              # NextAuth configuration
  auth/helpers.ts      # Auth utilities
  auth/magic-link.ts   # Magic link logic
  billing/plans.ts     # Plan definitions
  subscriptions.ts     # Subscription helpers
  queries/             # Database query functions
  email.ts             # Email sending helpers

emails/            # React email templates

.cursor/
  rules/           # Cursor rules
  commands/        # Cursor commands

Route groups

(marketing) - Public pages

  • Simple header + footer layout
  • No authentication required
  • Example: / (home), /pricing

app/ - Protected pages (served at /app/*)

  • Uses AppShell component with sidebar navigation
  • All pages protected via requireUser() in layout
  • Example: /app/dashboard, /app/billing, /app/settings

Root-level pages

  • /login - Sign-in page (no auth required)
  • API routes under /api

Adding pages

Public marketing page

Create under app/(marketing)/:

app/(marketing)/about/page.tsx
// app/(marketing)/about/page.tsx
export default function AboutPage() {
  return <div>About us</div>;
}

Protected app page

Use the /add-protected-page command, or manually:

  1. Create page under app/app/[route]/page.tsx
  2. Add navigation link in components/app-shell.tsx
app/app/projects/page.tsx
// app/app/projects/page.tsx
import { requireUser } from "@/lib/auth/helpers";

export default async function ProjectsPage() {
  const user = await requireUser();
  return <div>Projects for {user.email}</div>;
}

Key conventions

  • Route groups don't affect URLs: (marketing)/pricing = /pricing
  • Protected pages always call requireUser() or rely on layout auth
  • API routes live under app/api/
  • Shared components go in components/
  • Feature logic goes in lib/
  • Database queries go in lib/queries/

Branding configuration

All branding is centralized in lib/config/site.ts:

export const siteConfig = {
  name: "My SaaS",
  tagline: "Ship faster",
  description: "Your product description",
  url: process.env.NEXT_PUBLIC_SITE_URL,
  email: {
    from: process.env.EMAIL_FROM_ADDRESS,
    fromName: process.env.EMAIL_FROM_NAME,
    support: "support@example.com",
  },
  logo: {
    default: "/logo.svg",
    dark: "/logo-dark.svg",
  },
};

This config is used by:

  • App shell header and navigation
  • Marketing pages
  • Email templates
  • SEO metadata