Skip to main content
Sabo uses Next.js 16 App Router with route groups for clean organization. This guide explains every major folder and file, helping you understand where to find and modify code.

High-Level Overview

sabo/
├── src/                  # Application source code
   ├── app/             # Next.js App Router (routes)
   ├── components/      # React components
   ├── lib/             # Libraries and utilities
   ├── hooks/           # Custom React hooks
   ├── content/         # MDX content (blog, changelog, legal)
   └── proxy.ts         # Top-level middleware
├── supabase/            # Database migrations
├── tests/               # Playwright E2E tests
├── public/              # Static assets
└── [config files]       # next.config.ts, tsconfig.json, etc.
Sabo follows Next.js conventions for file naming: page.tsx for routes, layout.tsx for layouts, route.ts for API endpoints.

Application Routes (src/app/)

Next.js App Router uses folders for routes. Sabo organizes routes with route groups (folders wrapped in parentheses) that don’t affect the URL structure:

Route Groups Explained

src/app/
├── (auth)/              # Authentication pages (public)
├── (dashboard)/         # Protected app area (requires login)
├── (marketing)/         # Public marketing pages
├── (legal)/             # Legal documents
├── blog/                # Blog system
├── changelog/           # Changelog system
├── api/                 # API endpoints
├── layout.tsx           # Root layout (applies to all pages)
└── not-found.tsx        # 404 page
Route groups like (auth) organize files without adding /auth to the URL. For example, (auth)/sign-in/page.tsx becomes /sign-in, not /auth/sign-in.

Authentication Routes ((auth)/)

Purpose: User authentication flows (sign-up, sign-in, password reset)
(auth)/
├── actions.ts                  # Server actions for auth (signIn, signUp, etc.)
├── sign-in/
   ├── page.tsx               # /sign-in
   └── confirm/
       └── page.tsx           # /sign-in/confirm (magic link confirmation)
├── sign-up/
   ├── page.tsx               # /sign-up
   └── confirm/
       └── page.tsx           # /sign-up/confirm (email verification prompt)
├── forgot-password/
   └── page.tsx               # /forgot-password
└── reset-password/
    └── page.tsx               # /reset-password
Key Files:
  • actions.ts - Server actions called by auth forms (handles Supabase API calls)
  • Each page.tsx - React Server Component rendering the auth UI
  • /confirm pages - Post-submission confirmation screens
Auth pages use src/components/auth/auth-page-layout.tsx for consistent styling.

Dashboard Routes ((dashboard)/)

Purpose: Protected application area (requires authentication)
(dashboard)/
└── dashboard/
    ├── layout.tsx              # Dashboard layout with sidebar
    ├── page.tsx                # /dashboard (main dashboard page)
    ├── data.json               # Sample chart/table data
    └── settings/
        ├── general/
   └── page.tsx        # /dashboard/settings/general
        ├── account/
   └── page.tsx        # /dashboard/settings/account
        ├── billing/
   └── page.tsx        # /dashboard/settings/billing
        └── notifications/
            └── page.tsx        # /dashboard/settings/notifications
Key Files:
  • layout.tsx - Sidebar layout wrapping all dashboard pages (uses SidebarProvider from shadcn/ui)
  • page.tsx - Dashboard homepage with sample cards, chart, and data table
  • settings/* - User settings pages connected to user_profiles and user_subscriptions tables
All routes under (dashboard)/ are protected by middleware. Unauthenticated users are redirected to /sign-in.

Marketing Routes ((marketing)/)

Purpose: Public marketing pages for your SaaS
(marketing)/
├── layout.tsx                  # Marketing layout (header + footer)
├── page.tsx                    # / (homepage)
├── pricing/
   └── page.tsx               # /pricing
└── contact/
    └── page.tsx               # /contact
Key Files:
  • page.tsx (root) - Homepage with hero, features, pricing, testimonials, FAQ, CTA
  • pricing/page.tsx - Full pricing page with plan comparison
  • contact/page.tsx - Contact form (submits to /api/contact)
The marketing layout (layout.tsx) includes the site header and footer, applied to all marketing pages.

Purpose: Legal documents rendered from MDX
(legal)/
├── layout.tsx                  # Legal pages layout
├── privacy/
   └── page.tsx               # /privacy
├── terms-of-service/
   └── page.tsx               # /terms-of-service
└── cookie-policy/
    └── page.tsx               # /cookie-policy
Content Source:
  • Pages fetch content from src/content/legal/*.mdx
  • MDX files use frontmatter for metadata (title, lastUpdated)

Blog Routes (blog/)

Purpose: MDX-based blog system
blog/
├── layout.tsx                  # Blog layout (no route group, URLs are /blog/*)
├── page.tsx                    # /blog (list all posts)
├── blog-client.tsx             # Client component for tag filtering
└── [slug]/
    └── page.tsx               # /blog/[slug] (individual post)
Content Source:
  • Blog posts in src/content/blog/*.mdx
  • Frontmatter: title, description, date, author, thumbnail, tags
  • Dynamic route [slug] matches MDX filename
To add a blog post, create a new .mdx file in src/content/blog/ with the required frontmatter.

Changelog Routes (changelog/)

Purpose: Version release notes
changelog/
├── layout.tsx                  # Changelog layout
├── page.tsx                    # /changelog (list all versions)
└── [slug]/
    └── page.tsx               # /changelog/[slug] (version details)
Content Source:
  • Changelog entries in src/content/changelog/*.mdx
  • Frontmatter: version, title, description, date
  • Entries sorted by date (newest first)

API Routes (api/)

Purpose: Backend API endpoints
api/
├── contact/
   └── route.ts               # POST /api/contact
├── checkout_sessions/
   └── route.ts               # POST /api/checkout_sessions (Stripe Checkout)
├── customer_portal/
   └── route.ts               # POST /api/customer_portal (Stripe portal)
└── webhooks/
    └── stripe/
        └── route.ts           # POST /api/webhooks/stripe
Key Files:
  • contact/route.ts - Contact form submission handler
  • checkout_sessions/route.ts - Creates Stripe Checkout session
  • customer_portal/route.ts - Generates Stripe Customer Portal URL
  • webhooks/stripe/route.ts - Handles Stripe webhook events (subscription lifecycle, invoices)
API routes export HTTP method handlers: export async function POST(request: Request) { ... }

Auth Callback (auth/)

Purpose: Supabase authentication callback handler
auth/
└── callback/
    └── route.ts               # GET /auth/callback
Handles:
  • OAuth redirects (Google, GitHub, Apple)
  • Email verification links
  • Magic link authentication
  • Password reset confirmations
This route is critical for auth flows. Ensure /auth/callback is added to Supabase Redirect URLs.

Root Files

app/
├── layout.tsx                  # Root layout (HTML, body, providers)
├── globals.css                 # Global Tailwind CSS styles
├── not-found.tsx               # 404 error page
├── favicon.ico                 # Site favicon
├── robots.ts                   # robots.txt generator
├── sitemap.ts                  # sitemap.xml generator
└── success/
    └── page.tsx               # /success (post-payment success page)
Key Files:
  • layout.tsx - Wraps all pages, includes <html>, <body>, and global providers (theme, auth, PostHog)
  • globals.css - Tailwind directives, CSS variables for theming, custom utilities
  • sitemap.ts - Generates dynamic sitemap including blog/changelog posts

Components (src/components/)

React components organized by feature:
components/
├── ui/                         # shadcn/ui + Magic UI (50+ components)
├── auth/                       # Authentication components
├── dashboard/                  # Dashboard-specific components
├── marketing/                  # Marketing page components
├── shared/                     # Shared components (header, footer, etc.)
├── example/                    # Demo components (for UI gallery)
├── theme-provider.tsx          # next-themes provider
└── posthog-provider.tsx        # PostHog analytics provider

UI Components (ui/)

50+ Components from shadcn/ui and Magic UI:
  • Base Components
  • Form Components
  • Overlays
  • Data Display
  • Magic UI (Animated)
  • Button, Badge, Card, Separator, Skeleton
  • Avatar, Tabs, Toggle, Progress, Spinner, KBD
All UI components use Tailwind CSS with CSS variables for theming. Modify theme colors in app/globals.css.

Auth Components (auth/)

auth/
├── auth-context.tsx            # Auth context provider (user state)
├── auth-page-layout.tsx        # Consistent layout for auth pages
└── oauth-buttons.tsx           # OAuth provider buttons (Google, GitHub, Apple)
Usage:
  • auth-context.tsx - Provides useAuth() hook for accessing user state
  • oauth-buttons.tsx - Reusable OAuth buttons called from sign-in/sign-up pages

Dashboard Components (dashboard/)

dashboard/
├── app-sidebar.tsx             # Main sidebar with navigation
├── nav-main.tsx                # Primary navigation items
├── nav-projects.tsx            # Project switcher items
├── nav-secondary.tsx           # Secondary navigation items
├── nav-user.tsx                # User menu in sidebar footer
├── team-switcher.tsx           # Team/workspace selector
├── header-user-menu.tsx        # User dropdown in header
├── notifications-dropdown.tsx  # Notifications bell dropdown
├── section-cards.tsx           # Dashboard stat cards
├── chart-area-interactive.tsx  # Interactive area chart (Recharts)
└── data-table.tsx              # Advanced data table (@tanstack/react-table)
Key Files:
  • app-sidebar.tsx - Composes all nav-* components into the full sidebar
  • data-table.tsx - Includes sorting, filtering, column visibility, and drag-drop

Marketing Components (marketing/)

marketing/
├── hero.tsx                    # Hero section with CTA
├── features-grid.tsx           # Features in grid layout
├── features-bento-grid.tsx     # Features in bento grid layout
├── features-accordion.tsx      # Features in accordion layout
├── social-proof.tsx            # Logo carousel (partner logos)
├── pricing.tsx                 # Pricing plans comparison
├── testimonials.tsx            # Customer testimonials
├── faq.tsx                     # FAQ accordion
├── cta.tsx                     # Call-to-action section
└── contact-form.tsx            # Contact form (with Zod validation)
Customization:
  • Edit text and copy directly in each component
  • Modify pricing plans in src/lib/payments/plans.ts
These components are composed together in (marketing)/page.tsx to create the homepage.

Shared Components (shared/)

shared/
├── header.tsx                  # Site header (marketing pages)
├── footer.tsx                  # Site footer
├── mobile-nav.tsx              # Mobile navigation drawer
├── logo.tsx                    # Site logo component
├── theme-toggle.tsx            # Dark/light mode toggle
├── mode-toggle.tsx             # Alternative theme toggle
├── status-badge.tsx            # Better Stack status badge
└── language-selector.tsx       # Language/locale selector
Key Files:
  • header.tsx - Desktop navigation with links
  • mobile-nav.tsx - Drawer navigation for mobile viewports
  • footer.tsx - Includes links, social icons, and status badge

Libraries (src/lib/)

Utility functions, clients, and integrations:
lib/
├── supabase/                   # Supabase clients and types
   ├── client.ts              # Browser client (createClient)
   ├── server.ts              # Server client (createClient, createServiceClient)
   ├── middleware.ts          # Auth middleware (updateSession, route protection)
   └── types.ts               # TypeScript types (UserProfile, UserSubscription, PaymentHistory)
├── payments/                   # Stripe integration
   ├── stripe.ts              # Stripe client instance
   ├── plans.ts               # Plan configuration + helpers
   └── index.ts               # Re-export entry point for payments module
├── posthog/                    # PostHog analytics
   ├── index.ts               # Re-exports
   └── server.ts              # Server-side PostHog client
├── posts.ts                    # Blog utilities (getAllPosts, getPostBySlug)
├── changelog.ts                # Changelog utilities (getAllChangelogs, getChangelogBySlug)
└── utils.ts                    # General utilities (cn for Tailwind classes)

Supabase Library (lib/supabase/)

Creates a Supabase client for use in Client Components:
import { createClient } from "@/lib/supabase/client";

export function MyComponent() {
  const supabase = createClient();
  // Use supabase.auth, supabase.from(), etc.
}
Exports two functions:
  • createClient() - Cookie-based client for Server Components, Server Actions
  • createServiceClient() - Service role client (admin access, bypasses RLS)
import { createClient } from "@/lib/supabase/server";

export default async function Page() {
  const supabase = await createClient();
  const { data } = await supabase.from('user_profiles').select();
  // ...
}
Exports updateSession() which:
  • Refreshes the user’s Supabase session
  • Redirects unauthenticated users from protected routes
  • Redirects authenticated users away from auth pages
Called from src/proxy.ts (top-level middleware).
TypeScript interfaces mirroring database schema:
  • UserProfile - user_profiles table
  • UserSubscription - user_subscriptions table
  • PaymentHistory - payment_history table
Import these for type-safe queries:
import type { UserProfile } from "@/lib/supabase/types";

Payments Library (lib/payments/)

Exports a configured Stripe instance:
import { stripe } from "@/lib/payments/stripe";

const session = await stripe.checkout.sessions.create({ ... });
Centralized plan definitions plus helper utilities:
export const plans: Plan[] = [
  { id: "free", name: "Free", monthlyPrice: 0, ... },
  { id: "pro", name: "Pro", monthlyPrice: 12, ... },
  { id: "enterprise", name: "Enterprise", monthlyPrice: null, ... }
];

export function getPlanById(planId: string): Plan | undefined
export function getPlanByPriceId(priceId: string): Plan | undefined
export function isYearlyPrice(priceId: string): boolean
export function getBillingCycle(priceId: string): "month" | "year"
export function formatPrice(amount: number): string
Used by pricing page, Stripe API routes, and webhook handlers.
Centralized exports so other modules can import stripe, plans, and helper functions from "@/lib/payments".

Content (src/content/)

MDX files for blog, changelog, and legal pages:
content/
├── blog/                       # Blog posts
   ├── getting-started.mdx
   ├── technical-deep-dive.mdx
   └── type-safe-forms-with-zod.mdx
├── changelog/                  # Version releases
   ├── 0-1-0.mdx
   ├── 0-2-0.mdx
   └── 0-3-0.mdx
└── legal/                      # Legal documents
    ├── privacy.mdx
    ├── terms-of-service.mdx
    └── cookie-policy.mdx
MDX Frontmatter Examples:
  • Blog Post
  • Changelog
---
title: "Getting Started with Sabo"
description: "Learn how to set up and deploy your SaaS in minutes"
date: "2024-01-15"
author:
  name: "John Doe"
  picture: "/blog/authors/john-doe.png"
thumbnail: "/blog/thumbnails/getting-started.png"
tags: ["tutorial", "setup"]
---
Add new content by creating a new .mdx file with the appropriate frontmatter. No code changes needed!

Hooks (src/hooks/)

Custom React hooks:
hooks/
├── use-media-query.ts          # Responsive breakpoint hook (SSR-safe)
└── use-mobile.ts               # Mobile viewport hook (768px breakpoint)
Usage Example:
import { useIsMobile } from "@/hooks/use-mobile";

export function MyComponent() {
  const isMobile = useIsMobile();

  return isMobile ? <MobileView /> : <DesktopView />;
}

Database (supabase/)

Supabase database configuration:
supabase/
├── migrations/
   └── 20240101000000_create_user_profiles.sql
└── README.md
Migration Creates:
  • user_profiles table (profile data, preferences)
  • user_subscriptions table (Stripe subscription sync)
  • payment_history table (payment records)
  • stripe_products table (product catalog)
  • RLS policies for all tables
  • profile-images storage bucket
Run this migration in your Supabase Dashboard (SQL Editor) or via Supabase CLI (supabase db push).

Tests (tests/e2e/)

Playwright end-to-end tests:
tests/
└── e2e/
    ├── auth/                   # Authentication tests (3 files)
    ├── dashboard/              # Dashboard tests (5 files)
    ├── marketing/              # Marketing page tests (3 files)
    ├── blog/                   # Blog tests (2 files)
    ├── changelog/              # Changelog tests (2 files)
    ├── legal/                  # Legal page tests (1 file)
    └── helpers/
        └── auth.ts             # Test helpers (setupAuthenticatedUser)
Run Tests:
pnpm test:e2e          # Headless mode
pnpm test:e2e:ui       # UI mode (interactive)
pnpm test:e2e:headed   # Browser visible
Tests run across Chromium, Firefox, WebKit, and mobile viewports (Pixel 5, iPhone 12).

Static Assets (public/)

Images, logos, and Open Graph images:
public/
├── logo.png                    # Site logo
├── logos/                      # Tech stack logos (Stripe, Supabase, etc.)
├── blog/
   ├── authors/               # Author profile pictures
   └── thumbnails/            # Blog post thumbnails
└── og/                         # Open Graph images for social sharing
Files in public/ are served at the root path. Reference as /logo.png in your code.

Configuration Files

sabo/
├── next.config.ts              # Next.js configuration
├── tsconfig.json               # TypeScript configuration
├── eslint.config.mjs           # ESLint configuration
├── playwright.config.ts        # Playwright test configuration
├── postcss.config.mjs          # PostCSS configuration (Tailwind)
├── components.json             # shadcn/ui configuration
├── package.json                # Dependencies and scripts
└── pnpm-lock.yaml              # Lockfile

File Naming Conventions

Sabo follows Next.js and React best practices:
TypeConventionExample
Routespage.tsx(dashboard)/dashboard/page.tsx
Layoutslayout.tsx(marketing)/layout.tsx
API Routesroute.tsapi/contact/route.ts
Componentskebab-case.tsxcontact-form.tsx
Hooksuse-*.tsuse-mobile.ts
Utilitieskebab-case.tsutils.ts
MDX Contentkebab-case.mdxgetting-started.mdx

Key Architectural Decisions

Route groups ((auth), (dashboard), etc.) organize code without affecting URLs. This keeps the file structure clean while maintaining short, user-friendly paths like /sign-in instead of /auth/sign-in.
Next.js Server Components and Client Components require different Supabase clients:
  • Server client (server.ts) uses cookies for SSR
  • Browser client (client.ts) works in Client Components
This separation prevents hydration mismatches and ensures auth works correctly.
Defining plans in one place makes it easy to:
  • Update pricing without touching multiple files
  • Add/remove plans
  • Sync plan data between UI, API, and webhooks
  • Type-check plan references
Server Actions (actions.ts) provide a secure way to call Supabase APIs from Client Components without exposing API logic to the browser. They’re called like regular functions but execute server-side.