A modern, production-ready SaaS starter built with Next.js 15, React Query, Supabase, and shadcn/ui. This comprehensive starter includes everything you need to launch a scalable SaaS application with enterprise-grade features.
π¨ Using this template? Make sure to follow the setup instructions carefully, especially the step to remove the
.gitdirectory after cloning. This prevents accidentally pushing your project's changes back to this template repository.
- Authentication: Complete auth system with login, signup, password reset
- Todo Management: Full CRUD operations with categories, priorities, and due dates
- Real-time Updates: Live synchronization across devices using Supabase
- Professional UI: Modern, responsive design with dark mode support
- Type Safety: End-to-end TypeScript with strict mode
- Performance: Optimized with React Query caching and optimistic updates
- Analytics: Vercel Analytics and Speed Insights (automatic on Vercel)
- SEO Optimization: Dynamic metadata, Open Graph, Twitter Cards, and sitemaps
- Environment Validation: Zod-based environment variable validation
- Error Handling: React error boundaries with extensible error reporting
- Testing: Comprehensive test suite with Jest and React Testing Library
- Documentation: Extensive inline documentation and architecture guides
- Type Safety: Strict TypeScript with end-to-end type safety
- Code Quality: ESLint configuration and best practices
- Framework: Next.js 15 (App Router)
- Language: TypeScript 5 (strict mode)
- Database: Supabase (PostgreSQL)
- Authentication: Supabase Auth
- State Management: TanStack React Query v5
- UI Components: shadcn/ui (Radix UI + Tailwind CSS)
- Styling: Tailwind CSS v4
- Forms: React Hook Form + Zod validation
- Background Jobs: Inngest (durable workflows, queues, cron)
- Analytics: Vercel Analytics & Speed Insights (automatic on Vercel)
- SEO: Dynamic metadata generation and sitemaps
- Testing: Jest + React Testing Library
- Deployment: Vercel (optimized configuration)
- Node.js 18+
- npm or yarn
- Docker (for local Supabase)
- Supabase account (for production)
# Clone the template
git clone https://github.com/zphelps/next-launch-ts.git your-project-name
cd your-project-name
# Install dependencies
npm install
# Disconnect from the template repository
git remote remove origin
# Create your own repository on GitHub, then add it as origin:
git remote add origin https://github.com/yourusername/your-project-name.git
git branch -M main
git push -u origin main
β οΈ Important: Always rungit remote remove originafter cloning to disconnect from the template repository. This prevents accidentally pushing your project changes back to the template.
# Start local Supabase stack (PostgreSQL, Auth, Storage, etc.)
npm run db:start
# This will output your local API credentials
# API URL: http://127.0.0.1:54321
# anon key: <your-local-anon-key>The first time you run this, it will download the necessary Docker images. This may take a few minutes.
# Copy the environment template
cp .env.example .env.local
# Edit .env.local with the credentials from step 2:
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-local-anon-key>
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# Optional - Customize SEO:
# NEXT_PUBLIC_SITE_NAME=Your App Name
# NEXT_PUBLIC_SITE_DESCRIPTION=Your app descriptionπ‘ Tip: Check
.env.examplefor a complete list of available configuration options.
npm run devOpen http://localhost:3000 to see your application.
While your local Supabase is running, you can access the Studio dashboard at:
http://127.0.0.1:54323
Here you can view your database tables, manage authentication, and more.
This project uses local Supabase for development, giving you:
- β‘ Instant database operations (no network latency)
- π Version-controlled schema migrations
- πΏ Preview environments per Git branch
- π§ͺ Safe testing without affecting production
# Start local Supabase
npm run db:start
# Start Inngest Dev Server (for background jobs)
npx inngest-cli@latest dev
# Stop local Supabase
npm run db:stop
# Reset local database (apply all migrations from scratch)
npm run db:reset
# Create a new migration
npm run db:migration <migration_name>
# Push migrations to production
npm run db:pushMethod 1: Create Migration Files (Recommended)
# Create a new migration
npm run db:migration add_products_table
# Edit the file in supabase/migrations/
# Then apply it locally:
npm run db:resetMethod 2: Use Studio Dashboard
# Make changes in Studio (http://127.0.0.1:54323)
# Then generate a migration from your changes:
npx supabase db diff -f describe_your_changesMigrations are automatically applied when you:
- Push to GitHub (if using Supabase branching)
- Merge to main branch (production deployment)
- Or manually run
npm run db:push
- Vercel Analytics: Automatic page views and performance tracking (works automatically on Vercel)
- Speed Insights: Core Web Vitals monitoring (works automatically on Vercel)
- Dynamic SEO: Flexible metadata generation for all pages
- Open Graph: Social media sharing optimization
- Twitter Cards: Enhanced Twitter sharing
- Sitemap: Automatic sitemap generation
- Robots.txt: Search engine optimization
- Zod Validation: Runtime environment variable validation
- Type Safety: Fully typed environment configuration
- Clear Documentation: Comprehensive
.env.examplewith all options
- Error Boundaries: React error boundaries with extensible error reporting
- Performance Optimization: React Query caching and optimistic updates
/
βββ supabase/ # Supabase configuration
β βββ config.toml # CLI configuration
β βββ migrations/ # Database migrations
βββ docs/
β βββ supabase/ # Supabase development guides
βββ src/
β βββ app/ # Next.js App Router
β β βββ (auth)/ # Authentication pages
β β βββ (dashboard)/ # Protected dashboard routes
β β βββ api/ # API route handlers
β β βββ sitemap.ts # Dynamic sitemap generation
β β βββ globals.css # Global styles
β βββ components/ # Shared UI components
β β βββ ui/ # shadcn/ui components
β βββ lib/ # Core utilities
β β βββ supabase/ # Supabase client
β β βββ inngest/ # Inngest client and functions
β β βββ env.ts # Environment validation
β β βββ seo.ts # SEO utilities
β βββ modules/ # Feature modules
β β βββ auth/ # Authentication module
β β βββ todos/ # Todo management module
β β βββ users/ # User management module
β βββ hooks/ # Shared custom hooks
β βββ middleware.ts # Route protection
This project follows a modular architecture pattern where each feature is self-contained:
src/modules/{feature}/
βββ components/ # Feature-specific React components
βββ hooks/ # Custom React Query hooks
βββ services/ # Business logic and data access
βββ types.ts # TypeScript interfaces
βββ config.ts # Module configuration
Component β Custom Hook β Service β Supabase
This ensures clear separation of concerns and makes the codebase maintainable and testable.
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Generate coverage report
npm run test:coverage-
Create Supabase Production Project:
- Go to supabase.com
- Create a new project
- Copy your production URL and anon key
-
Link to Production Database:
npx supabase link --project-ref your-project-id
-
Push Migrations to Production:
npm run db:push
- Push your code to GitHub
- Connect your repository to Vercel
- Add your production environment variables in Vercel dashboard:
NEXT_PUBLIC_SUPABASE_URL- Your production Supabase URLNEXT_PUBLIC_SUPABASE_ANON_KEY- Your production anon keyNEXT_PUBLIC_SITE_URL- Your production domain
- Deploy!
Option 1: Automatic (Recommended)
- Enable Supabase GitHub integration
- Migrations auto-apply when merging to main
Option 2: Manual
npm run db:pushπ‘ Tip: Analytics work automatically when deployed to Vercel - no configuration needed!
- JWT-based authentication with Supabase
- Protected routes using middleware
- Automatic token refresh
- Secure user session management
- React Query for server state
- React Context for authentication state
- Optimistic updates for better UX
- React Hook Form for performance
- Zod schemas for validation
- Type-safe form data
- Global error boundaries for graceful error handling
- Toast notifications for user feedback
- Extensible error reporting (ready for Sentry, LogRocket, etc.)
- Durable functions: Multi-step workflows with automatic retries
- Event-driven: Trigger functions from anywhere in your app
- Scheduled jobs: Cron-based tasks for cleanup, reports, etc.
- Local development: Inngest Dev Server for testing workflows
- Zero infrastructure: No queues or workers to manage
- Use
generateSEO()utility for consistent metadata - Automatic sitemap generation for all routes
- Open Graph and Twitter Card optimization
- Dynamic metadata based on page content
- Vercel Analytics work automatically when deployed to Vercel
- Enable Web Analytics and Speed Insights in your Vercel dashboard
- No configuration or environment variables needed
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
import { generateMetadata, pageMetadata } from '@/lib/seo';
// Use predefined metadata for common pages
export const metadata = pageMetadata.login;
// Or create custom metadata for specific pages
export const metadata = generateMetadata({
title: 'My Custom Page',
description: 'This is a custom page with optimized SEO',
image: '/custom-og-image.png',
});import { env } from '@/lib/env';
// Access validated environment variables
const supabaseUrl = env.NEXT_PUBLIC_SUPABASE_URL;
const siteName = env.NEXT_PUBLIC_SITE_NAME || 'Default Name';import ErrorBoundary from '@/components/error-boundary';
export function MyApp() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}// Create a durable function with steps
import { inngest } from "@/lib/inngest/client";
export const processOrder = inngest.createFunction(
{ id: "process-order", retries: 3 },
{ event: "order/created" },
async ({ event, step }) => {
// Each step is independently retried on failure
await step.run("validate-inventory", async () => {
return await checkInventory(event.data.items);
});
await step.run("charge-payment", async () => {
return await chargeCard(event.data.paymentId);
});
// Durable sleep - function pauses without blocking
await step.sleep("wait-for-processing", "5m");
await step.run("send-confirmation", async () => {
return await sendEmail(event.data.email);
});
}
);
// Trigger the function from anywhere
await inngest.send({
name: "order/created",
data: { orderId: "123", items: [...], email: "user@example.com" }
});