Launch MVP is Sean Chen's open source SaaS boilerplate with 970+ stars. It includes Stripe subscriptions, email via Resend, and a built-in trial system.
We ran it through a comprehensive PLG skills audit. This is the highest-scoring Supabase starter we've analyzed.
Overall PLG Score: 5.9/10 — The best foundation for PLG among Supabase starters. Trial system works, onboarding exists (but disabled), PostHog is configured (but commented out).
The full audit
| Category | Score | Key finding |
|---|---|---|
| Signup flow | 7.5/10 | Email + Google OAuth, email verification flow |
| Feature gating | 6/10 | Dashboard gated by subscription/trial status |
| Trial optimization | 6.5/10 | 48-hour auto-trial, proper expiry handling |
| Product analytics | 5/10 | PostHog configured but disabled |
| Self-serve motion | 7/10 | Stripe Buy Button, cancel/reactivate flows |
| Onboarding | 6.5/10 | Tour component exists, database tracking, but disabled |
| Paywall/upgrade CRO | 5/10 | Profile page paywall, needs copy optimization |
1. Signup flow analysis (7.5/10)
Form implementation
Location: /components/LoginForm.tsx (Lines 1-128)
// Lines 75-91
<form onSubmit={handleSubmit} className="space-y-6">
<input type="email" placeholder="Email address" />
<input type="password" placeholder="Password" />
</form>
Form fields: 2 required (email, password), 0 optional — minimal friction.
Authentication options
Email + Password:
- Standard Supabase Auth implementation
- Toggle between login/signup modes
Google OAuth:
Location: /contexts/AuthContext.tsx (Lines 143-149)
const signInWithGoogle = async () => {
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${window.location.origin}/auth/callback` }
});
};
Missing providers: No GitHub, Microsoft, or other OAuth options.
Email verification
Location: /app/verify-email/page.tsx (Lines 1-88)
// Lines 13-34
const [countdown, setCountdown] = useState(60);
// 60-second countdown before resend available
// Resend logic marked as TODO (line 34)
The verification flow:
- User signs up
- Redirected to
/verify-emailif!data.user.email_confirmed_at - 60-second countdown before resend option
What's missing
No form validation library:
- No Zod, Yup, or React Hook Form
- Basic HTML5 validation only
No signup analytics:
- No tracking events for signup funnel
identifyUser()exists but is never called
2. Feature gating audit (6/10)
Pricing tiers
Location: /components/PricingSection.tsx (Lines 14-63)
const pricingTiers = [
{ id: "pro", name: "Pro", price: "$19/month" },
{ id: "enterprise", name: "Enterprise", price: "$49/month", popular: true },
{ id: "custom", name: "Custom", price: "Custom", cta: "Contact Sales" }
];
Each tier lists features:
- Pro: Templates, Priority support, Custom branding, Analytics, Team collaboration
- Enterprise: Everything in Pro + Advanced security, Custom integrations, 24/7 support, SLA
- Custom: Custom development, Dedicated support, On-premise, Training
Access control
Location: /app/dashboard/page.tsx (Lines 106-124)
const hasValidSubscription = ['active', 'trialing'].includes(subscription?.status || '');
if (!hasValidSubscription && !isInTrial) {
router.replace('/profile');
}
The dashboard is gated — users without a subscription or trial are redirected to /profile.
What's missing
No feature-level gating:
- Entire dashboard is gated, not individual features
- No
hasFeature()orcanAccess()checks - No "Upgrade to unlock" messaging for specific features
- No usage quotas or limits
3. Trial optimization (6.5/10)
Trial implementation
Location: /hooks/useTrialStatus.ts (Lines 1-97)
// Lines 60-62
const trialEndTime = new Date();
trialEndTime.setHours(trialEndTime.getHours() + 48); // 48-hour trial
Trial type: Opt-out (auto-created, no card required)
- New users automatically get 48-hour trial on first dashboard visit
- Trial stored in
user_trialstable
Trial flow:
- User accesses dashboard
- Hook checks if trial exists
- If not, creates 48-hour trial via upsert
- Stores
trial_end_time,is_trial_used,trial_start_time
Trial expiry handling
Location: /app/profile/page.tsx (Lines 222-240)
{isInTrial ? (
<p>Your trial will end on {trialEndTime}</p>
) : trialEndTime ? (
<p>Your trial period ended on {trialEndTime}</p>
) : (
<p>Subscribe to unlock the cooking experience</p>
)}
The UI properly handles:
- Active trial state
- Expired trial state
- Never-had-trial state
What's missing
No trial emails:
- No welcome email mentioning trial
- No reminder at 24 hours
- No expiry notification
The webhook handles customer.subscription.trial_will_end but doesn't send emails:
// /app/api/stripe/webhook/route.ts Lines 216-219
case 'customer.subscription.trial_will_end':
// No email action implemented
break;
No trial extension logic.
4. Product analytics (5/10)
PostHog infrastructure
Location: /utils/posthog.ts (Lines 1-33)
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://app.posthog.com',
autocapture: false,
capture_pageview: false,
persistence: 'localStorage'
});
Location: /contexts/PostHogContext.tsx (Lines 1-30)
PostHog provider is configured with:
PostHogProvidercomponent- Page view tracking component
- User identification utilities
Tracking utilities
Location: /utils/analytics.ts (Lines 1-27)
export const trackEvent = (eventName: string, properties?: EventProperties) => {
if (posthog && posthog.capture) {
posthog.capture(eventName, properties);
}
};
export const identifyUser = (userId: string, properties?: EventProperties) => {
if (posthog && posthog.identify) {
posthog.identify(userId, properties);
}
};
The critical problem
PostHog is disabled:
Location: /app/layout.tsx (Lines 23-32)
// <PostHogProvider>
// {children}
// </PostHogProvider>
The entire PostHog provider is commented out.
No events are tracked:
trackEvent()is defined but never calledidentifyUser()is defined but never called- Only
$pageviewwould be captured (if enabled)
What you'd get by enabling
Uncomment the PostHog provider and you immediately get:
- Page view tracking
- Session recording capability
- Feature flag infrastructure
- A/B testing capability
But you'd still need to add custom events for signup, payment, and feature usage.
5. Self-serve motion (7/10)
Checkout flow
Location: /app/pay/page.tsx (Lines 1-80)
Entry points:
- Direct URL:
/pay - CTA from profile page (trial expiry)
- CTA from pricing section
Checkout component:
Location: /components/StripeBuyButton.tsx (Lines 1-66)
<stripe-buy-button
buy-button-id="${buyButtonId}"
publishable-key="${publishableKey}"
client-reference-id="${user.id}"
customer-email="${user.email}"
success-url="${NEXT_PUBLIC_APP_URL}/profile?payment=success"
cancel-url="${NEXT_PUBLIC_APP_URL}/pay?canceled=true"
/>
Uses Stripe's embedded Buy Button — minimal checkout friction.
Billing management
Location: /app/profile/page.tsx (Lines 170-289)
Features implemented:
-
Status display (Lines 181-187)
- Status badge (active, trialing, canceled)
- Start date, period end date
-
Cancellation (Lines 256-289)
- Modal confirmation required
- Preserves access until period end
- API:
POST /api/stripe/cancel
-
Reactivation (Lines 199-209)
- Resume button for canceled subscriptions
- API:
POST /api/stripe/reactivate
What's missing
No team/seat management:
- Single user per subscription
- No
invitefunctionality - No team tables
No invoice management:
- No invoice history in-app
- Delegated to Stripe
6. Onboarding audit (6.5/10)
Onboarding tour component
Location: /components/OnboardingTour.tsx (Lines 1-205)
This is the most complete onboarding implementation among the templates we analyzed:
// Lines 20-66 — Tour steps definition
const steps = [
{ title: "Start with a Template", targetClass: "recipe-templates" },
{ title: "Voice Start", targetClass: "ai-assistant-button" },
{ title: "Voice Stop", targetClass: "ai-assistant-button" },
{ title: "Add Your Own Recipe", targetClass: "add-recipe-button" }
];
Features:
- Multi-step guided tour
- Element highlighting with CSS classes
- Smooth scrolling to target elements
- Progress indicator ("X of Y steps")
- Next/Previous navigation
State persistence
Location: Database table user_preferences
CREATE TABLE public.user_preferences (
user_id uuid NOT NULL,
has_completed_onboarding boolean DEFAULT false,
created_at timestamp,
updated_at timestamp
);
Completion tracking:
// OnboardingTour.tsx Lines 124-132
await supabase.from('user_preferences').upsert({
user_id: user.id,
has_completed_onboarding: true
}, { onConflict: 'user_id' });
The problem
Onboarding is disabled:
Location: /app/dashboard/page.tsx (Line 11)
// import OnboardingTour from '@/components/OnboardingTour';
The import is commented out. The full onboarding system exists but isn't active.
What you'd get by enabling
Uncomment the import and render the component:
- First-time users see guided tour
- Tour completion tracked in database
- Users won't see tour again after completion
7. Paywall & upgrade CRO (5/10)
Paywall location
Location: /app/profile/page.tsx (Lines 149-290)
The profile page serves as the paywall for:
- Users with expired trial
- Users without subscription
- Users who canceled
Paywall copy
Trial active:
"You are currently in your 48-hour trial period.
Your trial will end on {date}."
Trial expired:
"Your trial period ended on {date}.
Subscribe now to regain access to the cooking experience."
CTA:
"Subscribe now to unlock the amazing cooking experience"
What's missing
No feature-level paywalls:
- No "Upgrade to unlock" on specific features
- No usage limit displays
No upgrade prompts:
- No contextual upsells during product usage
- No milestone-based upgrade suggestions
No price anchoring:
- Stripe Buy Button shows price, but no comparison
- No "Save 20% with annual" messaging
Database schema
Tables for PLG
| Table | Purpose |
|---|---|
user_preferences | Onboarding state (has_completed_onboarding) |
user_trials | Trial tracking (trial_end_time, is_trial_used) |
subscriptions | Subscription state synced from Stripe |
Missing tables
- No
feature_usagefor usage tracking - No
teamsororganizations - No
analytics_eventsfor custom tracking
Email infrastructure
Location: /utils/resend.ts and /app/api/email/ routes
Resend is configured with email templates for:
- Welcome email on signup
- Billing confirmation
- Cancellation notification
Missing:
- Trial reminder emails
- Trial expiry emails
- Re-engagement emails
Quick wins: Enable what's already built
1. Enable PostHog
File: /app/layout.tsx
// Uncomment these lines:
<PostHogProvider>
{children}
</PostHogProvider>
You'll immediately get page views and session tracking.
2. Enable onboarding tour
File: /app/dashboard/page.tsx
// Uncomment:
import OnboardingTour from '@/components/OnboardingTour';
// Add to render:
{!hasCompletedOnboarding && <OnboardingTour />}
3. Add user identification
File: /contexts/AuthContext.tsx
After successful login, add:
import { identifyUser } from '@/utils/analytics';
// After auth success:
identifyUser(user.id, { email: user.email });
What still needs work
Priority 1: Add custom event tracking
// Signup funnel
track('signup_started');
track('signup_completed');
track('email_verified');
// Trial funnel
track('trial_started');
track('trial_expired');
// Payment funnel
track('checkout_started');
track('checkout_completed');
track('subscription_canceled');
Priority 2: Add trial emails
Create email templates for:
- Trial welcome (day 0)
- Trial reminder (24 hours before expiry)
- Trial expired (day 2)
Priority 3: Feature-level gating
Replace dashboard-level gating with feature-level:
function canAccessFeature(user, feature) {
if (isPro(user)) return true;
if (isInTrial(user)) return TRIAL_FEATURES.includes(feature);
return FREE_FEATURES.includes(feature);
}
Comparison to other starters
| Template | PLG score | Best feature |
|---|---|---|
| Launch MVP | 5.9/10 | Trial system + onboarding (disabled) |
| Vercel Subscription Payments | 3/10 | Stripe billing infrastructure |
| Nextbase | 3.2/10 | Signup flow (8/10) |
Launch MVP is the only template with:
- Working trial system
- Onboarding component with database tracking
- Analytics infrastructure (even if disabled)
- Cancel/reactivate subscription flows
First steps to PLG
Launch MVP has the best foundation — here's how to activate it:
Day 1: Enable what's already built
- Uncomment PostHog in
/app/providers.tsx - Uncomment the onboarding import in
/app/dashboard/page.tsx - Add your PostHog project key to
.env - You now have analytics and onboarding working
Week 1: Add custom event tracking
The PostHog provider is there, now use it:
// After successful signup
posthog.capture('signup_completed', { method: 'email' });
// When trial starts
posthog.capture('trial_started', { plan: 'pro' });
// When user completes key action
posthog.capture('aha_moment_reached', { action: 'first_project_created' });
// When trial converts
posthog.capture('trial_converted', { plan: 'pro', trial_days_used: 7 });
Week 2: Customize onboarding for your product
- Edit
/components/OnboardingTour.tsxwith your actual features - Point users to their first "aha moment" action
- Track completion:
posthog.capture('onboarding_completed') - A/B test different tour lengths
Week 3: Add feature-level gating
Move beyond dashboard-level access:
// Check subscription for specific features
const canExport = subscription?.plan === 'pro';
{canExport ? (
<ExportButton />
) : (
<UpgradePrompt feature="export" />
)}
Week 4: Optimize trial conversion
- Add trial countdown banner in dashboard
- Send email on day 7: "You've used X features so far"
- Send email on day 12: "Your trial ends in 2 days"
- Create a "What you'll lose" summary for expiring trials
When to use Launch MVP
Good fit:
- Monetized SaaS with subscriptions
- Products needing trial-to-paid conversion
- Teams who want PLG infrastructure with minimal setup
Considerations:
- PostHog is disabled by default
- Onboarding needs customization for your product
- Feature gating is binary (dashboard or not)
- No team/seat management
Conclusion
Launch MVP scores 5.9/10 for PLG readiness — the highest among Supabase starters.
The infrastructure exists:
- Trial system works
- Onboarding component is ready
- PostHog is configured
- Cancel/reactivate flows are implemented
The main issue: critical features are disabled. Two quick changes (uncommenting PostHog and the onboarding import) would significantly improve the user experience.
For a monetized SaaS, this is the strongest starting point. Add custom event tracking, enable the built-in features, and you'll have a genuinely PLG-capable foundation.
Appendix: Key file locations
| Component | File |
|---|---|
| Login form | /components/LoginForm.tsx |
| Auth context | /contexts/AuthContext.tsx |
| Email verification | /app/verify-email/page.tsx |
| Dashboard (gated) | /app/dashboard/page.tsx |
| Profile/paywall | /app/profile/page.tsx |
| Pricing section | /components/PricingSection.tsx |
| Trial hook | /hooks/useTrialStatus.ts |
| Subscription hook | /hooks/useSubscription.ts |
| Onboarding tour | /components/OnboardingTour.tsx |
| PostHog config | /utils/posthog.ts |
| PostHog context | /contexts/PostHogContext.tsx |
| Analytics utilities | /utils/analytics.ts |
| Stripe buy button | /components/StripeBuyButton.tsx |
| Stripe webhook | /app/api/stripe/webhook/route.ts |
This analysis was performed using PLG Skills, an open-source framework for product-led growth audits. Skills used: signup-flow-cro, feature-gating, trial-optimization, product-analytics, self-serve-motion, paywall-upgrade-cro, product-onboarding.
Frequently asked questions
Is Launch MVP the best Supabase SaaS starter?
For PLG, yes. Launch MVP scores 5.9/10 — the highest among Supabase starters we analyzed. It has a working 48-hour trial system, an onboarding tour component, PostHog analytics configuration, and cancel/reactivate subscription flows. The main issue: some features are disabled by default.
Does Launch MVP include free trials?
Yes. Launch MVP has a working 48-hour auto-trial system stored in the user_trials table. New users automatically get a trial on first dashboard visit — no credit card required. The trial expiry is properly handled with clear UI messaging for active, expired, and never-had-trial states.
Why is PostHog disabled in Launch MVP?
The PostHog provider is commented out in /app/layout.tsx by default. The infrastructure is fully configured — posthog.init(), trackEvent(), identifyUser() — but not active. Uncomment the <PostHogProvider> wrapper to enable page views and session tracking immediately.
How do I enable the onboarding tour in Launch MVP?
The OnboardingTour component import is commented out in /app/dashboard/page.tsx. Uncomment the import and add {!hasCompletedOnboarding && <OnboardingTour />} to render the 4-step guided tour. Completion state is tracked in the user_preferences table.
Does Launch MVP support team billing?
No. Like other Supabase starters, Launch MVP is single-user per subscription. There are no team tables, invite functionality, or seat-based pricing. For team billing, you'll need to add teams and team_members tables and modify the Stripe integration.
What's missing in Launch MVP for production PLG?
Three things: (1) Custom event tracking — you need to add track() calls for signup, payment, and feature usage events. (2) Trial emails — no welcome, reminder, or expiry emails configured. (3) Feature-level gating — the dashboard is gated, but individual features aren't. See our Priority 1-3 recommendations in the audit.