Skene
BLOG

Launch MVP Review: Best Supabase SaaS Starter PLG Audit 2026

Is Launch MVP the best Supabase starter for SaaS? Highest PLG score (5.9/10) with working trials, onboarding, and PostHog analytics. See the full audit and what needs enabling.

·bySkene·LinkedIn
Summarize this article with LLMs

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

CategoryScoreKey finding
Signup flow7.5/10Email + Google OAuth, email verification flow
Feature gating6/10Dashboard gated by subscription/trial status
Trial optimization6.5/1048-hour auto-trial, proper expiry handling
Product analytics5/10PostHog configured but disabled
Self-serve motion7/10Stripe Buy Button, cancel/reactivate flows
Onboarding6.5/10Tour component exists, database tracking, but disabled
Paywall/upgrade CRO5/10Profile 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:

  1. User signs up
  2. Redirected to /verify-email if !data.user.email_confirmed_at
  3. 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() or canAccess() 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_trials table

Trial flow:

  1. User accesses dashboard
  2. Hook checks if trial exists
  3. If not, creates 48-hour trial via upsert
  4. 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:

  • PostHogProvider component
  • 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 called
  • identifyUser() is defined but never called
  • Only $pageview would 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:

  1. Status display (Lines 181-187)

    • Status badge (active, trialing, canceled)
    • Start date, period end date
  2. Cancellation (Lines 256-289)

    • Modal confirmation required
    • Preserves access until period end
    • API: POST /api/stripe/cancel
  3. 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 invite functionality
  • 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:

  1. First-time users see guided tour
  2. Tour completion tracked in database
  3. 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

TablePurpose
user_preferencesOnboarding state (has_completed_onboarding)
user_trialsTrial tracking (trial_end_time, is_trial_used)
subscriptionsSubscription state synced from Stripe

Missing tables

  • No feature_usage for usage tracking
  • No teams or organizations
  • No analytics_events for 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

TemplatePLG scoreBest feature
Launch MVP5.9/10Trial system + onboarding (disabled)
Vercel Subscription Payments3/10Stripe billing infrastructure
Nextbase3.2/10Signup 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

  1. Uncomment PostHog in /app/providers.tsx
  2. Uncomment the onboarding import in /app/dashboard/page.tsx
  3. Add your PostHog project key to .env
  4. 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

  1. Edit /components/OnboardingTour.tsx with your actual features
  2. Point users to their first "aha moment" action
  3. Track completion: posthog.capture('onboarding_completed')
  4. 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

  1. Add trial countdown banner in dashboard
  2. Send email on day 7: "You've used X features so far"
  3. Send email on day 12: "Your trial ends in 2 days"
  4. 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

ComponentFile
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.

Done with this article? Explore more ways to ship real PLG.