Back to skills
extension
Category: OtherAPI key required

Whirlwind

Production-ready Next.js AI SaaS template with Supabase auth, Stripe payments, email system, multi-model AI client, and customizable components for rapid AI...

personAuthor: rosebeattyhubclawhub

Whirlwind AI SaaS Template Skill

Version: 2.0.0

Overview

Whirlwind is a production-ready Next.js template for building AI SaaS products. It provides complete infrastructure so developers can focus on their unique AI features.

Included Infrastructure:

  • ✅ Supabase authentication (email, OAuth, magic links)
  • ✅ Stripe payments (subscriptions, one-time, webhooks)
  • ✅ Email system (Resend/Mailgun)
  • ✅ Multi-model AI client (Claude, GPT-4, Gemini)
  • ✅ Landing page components
  • ✅ Dashboard shell
  • ✅ Database with RLS

ClawdBot's Role: Customize the template for each user's specific AI product by generating custom components, API routes, and database tables.

File Structure

✅ Infrastructure (DON'T MODIFY)

These files are core infrastructure. Never modify:

lib/
  supabaseClient.js       - Database client setup
  stripe.js               - Stripe checkout/portal functions
  api.js                  - Axios wrapper with auth
  seo.js                  - SEO metadata helpers
  ai-client.js            - Multi-model AI client
  
components/
  ButtonCheckout.js       - Stripe checkout button
  ButtonSignin.js         - Authentication button
  ButtonAccount.js        - Account dropdown
  CheckoutModal.js        - Checkout modal
  LayoutClient.js         - Client-side layout wrapper
  SubscribeForm.js        - Email subscription
  ai/                     - Pre-built AI components
  providers/              - Auth/context providers
  
app/api/
  auth/                   - Supabase auth routes
  stripe/                 - Stripe webhook handlers
  webhook/                - Other webhooks

🎨 Customizable (UPDATE CONTENT)

Update these files with product-specific content:

components/
  Hero.js                 - Update hero title/subtitle
  Features.js             - Update features list
  Pricing.js              - Update pricing copy
  FAQ.js                  - Update FAQ items
  CTA.js                  - Update call-to-action
  Testimonials.js         - Add testimonials
  Header.js               - Update navigation (partial)
  Footer.js               - Update footer links (partial)
  
config.js                 - App configuration (Stripe plans, etc.)

🤖 Create New (CLAWDBOT GENERATES)

Generate new files in these locations:

components/features/      - Custom AI feature components
app/api/custom/          - Custom API endpoints
lib/supabase/migrations/ - Custom tables (002+)
config/site.js           - Product-specific configuration (NEW)

AI Client Usage

Whirlwind provides lib/ai-client.js for all AI operations. Always use this instead of creating ad-hoc clients.

Basic Usage

import AIClient from "@/lib/ai-client";

// Choose model: 'claude', 'openai', or 'gemini'
const ai = new AIClient('claude');

// Simple chat
const response = await ai.chat([
  { role: "user", content: "Your prompt here" }
]);

// With options
const response = await ai.chat(messages, {
  maxTokens: 2000
});

// Streaming
await ai.stream(messages, (chunk) => {
  console.log(chunk); // Handle each chunk
});

Available Models

  • claude - claude-sonnet-4-20250514 (recommended)
  • openai - gpt-4
  • gemini - gemini-pro (if configured)

Database Operations

Always use the Supabase client with proper error handling:

import { supabase } from "@/lib/supabaseClient";

// Insert
const { data, error } = await supabase
  .from('table_name')
  .insert({ column: value });

// Select with auth
const { data, error } = await supabase
  .from('table_name')
  .select('*')
  .eq('user_id', userId);

// Always check errors
if (error) {
  console.error(error);
  throw new Error(error.message);
}

Creating an AI Product - Step by Step

When a user says: "Create [product name] - [description]"

Step 1: Create config/site.js

This centralizes all product-specific content:

/**
 * 🎨 CLAWDBOT CUSTOMIZES
 * Product-specific configuration
 */

const siteConfig = {
  productName: "[Product Name]",
  productDescription: "[Brief description]",
  
  hero: {
    title: "[Compelling title]",
    subtitle: "[Subtitle explaining value]",
  },
  
  features: [
    {
      icon: "[emoji]",
      name: "[Feature name]",
      description: "[Feature description]"
    },
    // Add 3-6 features
  ]
};

export default siteConfig;

Step 2: Create Feature Component

Location: components/features/[ComponentName].jsx

Use this template structure:

/**
 * 🤖 CLAWDBOT CREATED
 * [Component description]
 */
"use client";

import { useState } from "react";
import apiClient from "@/lib/api";

export default function [ComponentName]() {
  const [input, setInput] = useState("");
  const [result, setResult] = useState("");
  const [loading, setLoading] = useState(false);

  const handleGenerate = async () => {
    setLoading(true);
    try {
      const response = await apiClient.post("/custom/[endpoint]", {
        input
      });
      setResult(response.result);
    } catch (error) {
      console.error("Error:", error);
    }
    setLoading(false);
  };

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h2 className="font-bebas text-4xl uppercase tracking-wide mb-6">
        [Component Title]
      </h2>
      
      {/* Input UI */}
      <div className="space-y-4">
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          className="w-full px-4 py-2 border border-gray-200 dark:border-white/10 rounded"
          placeholder="[Placeholder]"
        />
        
        <button
          onClick={handleGenerate}
          disabled={loading || !input}
          className="px-6 py-3 bg-primary hover:bg-primary-focus text-black font-bebas text-lg uppercase tracking-wide transition-colors disabled:opacity-50"
        >
          {loading ? "Processing..." : "[Action Text]"}
        </button>
      </div>
      
      {/* Output UI */}
      {result && (
        <div className="mt-6 p-4 bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded">
          <h3 className="font-bebas text-xl uppercase mb-2">Result:</h3>
          <div className="font-ibm text-sm">{result}</div>
        </div>
      )}
    </div>
  );
}

Step 3: Create API Route

Location: app/api/custom/[endpoint]/route.js

/**
 * 🤖 CLAWDBOT CREATED
 * [Endpoint description]
 */
import { NextResponse } from "next/server";
import AIClient from "@/lib/ai-client";
import { supabase } from "@/lib/supabaseClient";

export async function POST(req) {
  try {
    // Parse input
    const { input } = await req.json();
    
    // Validate
    if (!input) {
      return NextResponse.json(
        { error: "Input is required" },
        { status: 400 }
      );
    }

    // Create AI prompt
    const prompt = `[Your specific prompt template]: ${input}`;

    // Call AI
    const ai = new AIClient('claude');
    const result = await ai.chat([
      { role: "user", content: prompt }
    ]);

    // Optional: Save to database
    // const { data: session } = await supabase.auth.getSession();
    // if (session?.user) {
    //   await supabase.from('your_table').insert({
    //     user_id: session.user.id,
    //     input,
    //     result
    //   });
    // }

    return NextResponse.json({ 
      success: true,
      result 
    });

  } catch (error) {
    console.error("API error:", error);
    return NextResponse.json(
      { error: error.message || "Processing failed" },
      { status: 500 }
    );
  }
}

Step 4: Create Database Migration

Location: lib/supabase/migrations/00X_[table_name].sql

-- 🤖 CLAWDBOT CREATED
-- [Table description]

create table if not exists [table_name] (
  id uuid default uuid_generate_v4() primary key,
  user_id uuid references auth.users on delete cascade not null,
  [your_columns] text not null,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null
);

-- Indexes
create index [table_name]_user_id_idx on [table_name](user_id);
create index [table_name]_created_at_idx on [table_name](created_at desc);

-- Row Level Security
alter table [table_name] enable row level security;

-- Policies
create policy "Users can view own records"
  on [table_name] for select
  using (auth.uid() = user_id);

create policy "Users can insert own records"
  on [table_name] for insert
  with check (auth.uid() = user_id);

create policy "Users can delete own records"
  on [table_name] for delete
  using (auth.uid() = user_id);

Step 5: Update Dashboard

Add component to app/dashboard/page.js:

import [ComponentName] from "@/components/features/[ComponentName]";

export default function Dashboard() {
  return (
    <div>
      <h1 className="font-bebas text-5xl">Dashboard</h1>
      <[ComponentName] />
    </div>
  );
}

Code Style Guidelines

Typography

  • Headers: font-bebas text-4xl uppercase tracking-wide
  • Body: font-ibm text-sm
  • Always include dark mode classes

Colors

  • Primary action: bg-primary hover:bg-primary-focus
  • Text: text-gray-900 dark:text-white
  • Borders: border-gray-200 dark:border-white/10
  • Backgrounds: bg-white dark:bg-white/5

Components

  • Always use "use client" for interactive components
  • Include loading states
  • Handle errors gracefully
  • Validate inputs
  • Add disabled states for buttons

Rules and Constraints

NEVER:

  • Modify infrastructure files (lib/supabaseClient.js, lib/stripe.js, etc.)
  • Remove existing functionality
  • Create duplicate AI clients (use AIClient)
  • Forget Row Level Security in migrations
  • Skip error handling

ALWAYS:

  • Add ClawdBot marker comments (🤖 CLAWDBOT CREATED)
  • Use AIClient for AI operations
  • Include proper error handling
  • Validate user inputs
  • Use Next.js 14 patterns
  • Include TypeScript types if using TS
  • Test database queries
  • Use proper HTTP status codes
  • Include loading/disabled states

Common Patterns

Content Generation

const prompt = `Generate ${contentType} about: ${topic}
Tone: ${tone}
Length: ${length} words
Include: ${requirements}`;

const ai = new AIClient('claude');
const content = await ai.chat([
  { role: "user", content: prompt }
]);

Image Analysis

// For image analysis, include base64 image in prompt
const prompt = `Analyze this image and ${task}`;
// Note: Image handling requires additional setup

Document Processing

const prompt = `Extract ${dataPoints} from this document:
${documentText}

Return as JSON.`;

const ai = new AIClient('claude');
const result = await ai.chat([
  { role: "user", content: prompt }
]);

Chat/Conversation

// Maintain conversation history
const conversationHistory = [
  { role: "user", content: "Previous message" },
  { role: "assistant", content: "Previous response" },
  { role: "user", content: "Current message" }
];

const ai = new AIClient('claude');
const response = await ai.chat(conversationHistory);

Example Workflows

See /examples folder for complete implementations:

  • content-generator.md - WriteFlow content platform
  • image-analyzer.md - VisionAI image analysis
  • chatbot.md - TalkAI chatbot platform
  • document-processor.md - DocuMind document intelligence

Templates

See /templates folder for code templates to copy:

  • component.jsx - Feature component template
  • api-route.js - API endpoint template
  • migration.sql - Database migration template

Environment Variables

Remind users to add these to .env:

# Required
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=

# AI (at least one required)
ANTHROPIC_API_KEY=
OPENAI_API_KEY=

# Optional
RESEND_API_KEY=
GOOGLE_AI_API_KEY=

Testing

After generating code, suggest testing:

  1. Check component renders: npm run dev
  2. Test API endpoint with curl or Postman
  3. Run database migration in Supabase dashboard
  4. Test full workflow end-to-end

Support

For issues or questions:

  • Documentation: [GitHub repo]
  • Community: [Discord]
  • Email: support@whirlwindai.xyz