LogoNebaura Docs

Project Structure

This guide explains the folder structure and organization of the Warp boilerplate project.

Overview

your-app/
├── app/                    # Next.js app router
│   ├── api/               # API routes
│   │   └── webhooks/      # Whop webhook handlers
│   ├── dashboard/         # Protected dashboard
│   ├── discover/          # Discover/browse page
│   ├── experiences/       # Experience pages
│   ├── layout.tsx         # Root layout
│   ├── page.tsx           # Home page
│   └── globals.css        # Global styles
├── components/            # React components
│   └── example-components.tsx  # Example UI components
├── db/                    # Database (Drizzle ORM)
│   ├── schema.ts         # Database schema
│   └── index.ts          # Database client
├── lib/                   # Utility libraries
│   ├── whop-sdk.ts       # Whop SDK setup
│   └── whop/             # Whop API utilities
│       ├── access.ts
│       ├── checkout.ts
│       ├── companies.ts
│       ├── experiences.ts
│       ├── members.ts
│       ├── memberships.ts
│       ├── moderation.ts
│       ├── notifications.ts
│       ├── payments.ts
│       ├── plans.ts
│       ├── products.ts
│       ├── promo-codes.ts
│       ├── users.ts
│       ├── webhooks.ts
│       └── websockets.ts
├── biome.json             # Biome linter config
├── drizzle.config.ts      # Drizzle ORM config
├── next.config.ts         # Next.js config
├── package.json           # Dependencies
├── tailwind.config.ts     # Tailwind CSS config
└── tsconfig.json          # TypeScript config

Note: The actual structure depends on which UI variant you choose (coss-ui, frosted-ui, or headless). The structure above shows the shared foundation.

Key Directories

/app

Next.js 15 App Router directory containing all pages and routes.

Subdirectories:

  • /api/webhooks - Whop webhook handlers for events
  • /dashboard - Protected dashboard page
  • /discover - Discover/browse experiences
  • /experiences - Individual experience pages

Key Files:

  • layout.tsx - Root layout with providers
  • page.tsx - Home/landing page
  • globals.css - Global CSS and Tailwind imports

/components

Reusable React components. The structure varies by UI variant:

coss-ui variant:

  • Includes shadcn/ui components in /ui folder
  • components.json for shadcn configuration

frosted-ui variant:

  • example-components.tsx - Pre-built example components

headless variant:

  • Minimal structure, bring your own components

Adding shadcn/ui components (coss-ui):

npx shadcn@latest add button

/db

Database schema and client configuration using Drizzle ORM.

Files:

  • schema.ts - Database table definitions

    export const users = pgTable('users', {
      id: text('id').primaryKey(),
      email: text('email').notNull(),
      // ...
    });
  • index.ts - Database client initialization

    import { drizzle } from 'drizzle-orm/postgres-js';
    export const db = drizzle(client);

Usage:

import { db } from '@/db';
import { users } from '@/db/schema';

const allUsers = await db.select().from(users);

/lib

Core utility functions and SDK configurations.

Key Files:

  • whop-sdk.ts - Whop SDK setup
    import { WhopSDK } from '@whop/sdk';
    export const whop = new WhopSDK({ 
      apiKey: process.env.WHOP_API_KEY 
    });

/whop Subfolder:

Contains all Whop API utility functions organized by module:

lib/whop/
├── access.ts          # Access control
├── checkout.ts        # Checkout sessions
├── companies.ts       # Company management
├── experiences.ts     # Experience management
├── members.ts         # Member utilities
├── memberships.ts     # Subscription management
├── moderation.ts      # User moderation
├── notifications.ts   # Push notifications
├── payments.ts        # Payment processing
├── plans.ts           # Pricing plans
├── products.ts        # Product management
├── promo-codes.ts     # Promo code management
├── users.ts           # User utilities
├── webhooks.ts        # Webhook management
└── websockets.ts      # WebSocket utilities

Configuration Files

next.config.ts

Next.js configuration including:

  • Build settings
  • Environment variables
  • Redirects and rewrites
  • Image optimization
const config: NextConfig = {
  // Your configuration
};

drizzle.config.ts

Drizzle ORM configuration for database migrations:

export default {
  schema: './db/schema.ts',
  out: './drizzle',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!
  }
};

tailwind.config.ts

Tailwind CSS configuration:

const config: Config = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}'
  ],
  theme: {
    extend: {
      // Custom theme
    }
  }
};

biome.json

Biome linter and formatter configuration:

{
  "formatter": {
    "enabled": true,
    "indentStyle": "tab"
  },
  "linter": {
    "enabled": true,
    "rules": {
      // Linting rules
    }
  }
}

components.json

shadcn/ui configuration (only in coss-ui variant):

{
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "zinc"
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

Note: This file only exists in the coss-ui variant. The frosted-ui and headless variants do not include it.

Path Aliases

The project uses TypeScript path aliases for cleaner imports:

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Usage:

// ✅ Good - using alias
import { db } from '@/db';
import { getCurrentUser } from '@/lib/whop/users';
import { whop } from '@/lib/whop-sdk';

// ❌ Avoid - relative paths
import { db } from '../../db';

Environment Variables

Store environment variables in .env.local:

# Whop
WHOP_API_KEY=your_api_key
WHOP_CLIENT_ID=your_client_id
WHOP_CLIENT_SECRET=your_client_secret

# Database
DATABASE_URL=postgresql://...

# App
NEXT_PUBLIC_APP_URL=http://localhost:3000

Access in code:

// Server-side
const apiKey = process.env.WHOP_API_KEY;

// Client-side (must start with NEXT_PUBLIC_)
const appUrl = process.env.NEXT_PUBLIC_APP_URL;

Adding New Features

1. Add a New Page

Create a file in /app:

// app/pricing/page.tsx
export default function PricingPage() {
  return <div>Pricing</div>;
}

Accessible at: /pricing

2. Add a New Component

Create in /components:

// components/pricing-card.tsx
export function PricingCard({ plan }) {
  return <div>{plan.name}</div>;
}

3. Add a New API Route

Create in /app/api:

// app/api/my-route/route.ts
import { getCurrentUser } from '@/lib/whop/users';
import { headers } from 'next/headers';

export async function GET() {
  const user = await getCurrentUser(await headers());
  
  if (!user) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }
  
  return Response.json({ message: 'Hello!' });
}

Accessible at: /api/my-route

4. Add Database Tables

Update /db/schema.ts:

export const subscriptions = pgTable('subscriptions', {
  id: text('id').primaryKey(),
  userId: text('user_id').notNull(),
  planId: text('plan_id').notNull(),
  status: text('status').notNull(),
  createdAt: timestamp('created_at').defaultNow()
});

Generate and run migration:

pnpm db:generate
pnpm db:migrate

5. Add Utility Functions

Create in /lib/whop:

// lib/whop/custom.ts
import { whop } from '../whop-sdk';

export async function myCustomFunction(userId: string) {
  // Your custom logic using Whop SDK
  const user = await whop.users.get(userId);
  return user;
}

Import and use:

import { myCustomFunction } from '@/lib/whop/custom';

Best Practices

File Organization

// ✅ Good - organized by feature
app/
  dashboard/
    page.tsx
    analytics/
      page.tsx
    settings/
      page.tsx

// ❌ Avoid - flat structure
app/
  dashboard.tsx
  dashboard-analytics.tsx
  dashboard-settings.tsx

Component Structure

// ✅ Good - component with types
type PricingCardProps = {
  plan: Plan;
  onSelect: (id: string) => void;
};

export function PricingCard({ plan, onSelect }: PricingCardProps) {
  return <div onClick={() => onSelect(plan.id)}>{plan.name}</div>;
}

// ❌ Avoid - no types
export function PricingCard({ plan, onSelect }) {
  return <div>{plan.name}</div>;
}

Import Organization

// ✅ Good - organized imports
// 1. External dependencies
import { useState } from 'react';

// 2. Internal utilities
import { db } from '@/db';
import { getCurrentUser } from '@/lib/whop/users';
import { whop } from '@/lib/whop-sdk';

// 3. Types
import type { User } from '@/types';

// 4. Relative imports
import { MyComponent } from './my-component';