Skip to content

PLG Readiness Audit: CMSaasStarter — 3.7/10 #208

@teekoo5

Description

@teekoo5

PLG Readiness Audit: CMSaasStarter — 3.7/10

We audited this repo for product-led growth readiness using the open-source PLG Skills framework. Here's what we found across 6 categories.

Overall: 3.7/10 — Best Stripe integration of any SvelteKit starter — full checkout, automatic customer creation, billing portal, and webhooks that actually update state. But no analytics and no feature gating.

Category Score Finding
Signup flow 7/10 Supabase Auth UI with GitHub OAuth
Feature gating 2/10 Static pricing table, no runtime checks
Trial optimization 1/10 Stripe trialing status recognized, not implemented
Product analytics 0/10 Documentation explains how to add PostHog, nothing installed
Self-serve motion 9/10 Full Stripe checkout + billing portal + customer creation
Paywall/upgrade CRO 3/10 Pricing page with plan indicator, no in-app prompts

Self-serve billing: best of any SvelteKit starter (9/10)

Full Stripe checkout with automatic customer creation on first purchase:

// src/routes/(admin)/account/subscribe/[slug]/+page.server.ts
const stripeSession = await stripe.checkout.sessions.create({
  line_items: [{ price: params.slug, quantity: 1 }],
  customer: customerId,
  mode: "subscription",
  success_url: `${url.origin}/account`,
  cancel_url: `${url.origin}/account/billing`,
});

Billing portal gives users full self-serve management (upgrade, downgrade, cancel, update payment, view invoices). The pricing module shows a "Current Plan" badge for the active subscription:

<!-- src/routes/(marketing)/pricing/pricing_module.svelte -->
{#if plan.id === currentPlanId}
  <Badge>Current Plan</Badge>
{:else}
  <Button>{callToAction}</Button>
{/if}

Compared to JustShip (the other SvelteKit starter we reviewed), the webhooks here actually work — they update user state, not just log to console.

Feature gating: limits shown but never checked

The pricing table defines feature limits:

{ name: "Feature 3", freeString: "3", proString: "Unlimited" }

But nowhere in the code are these limits enforced. No hasFeature(), no route gating by plan, no usage counting, no locked UI components. All features are available to everyone regardless of plan.

Analytics: docs exist, code doesn't

There's an analytics_docs.md file explaining how to add PostHog or GA4. But no analytics package is in package.json and no tracking calls exist in the codebase. You can't measure signup-to-paid conversion without adding analytics yourself.

Trials: status-aware, not implemented

The subscription handling recognizes trialing as a valid active status:

const primaryStripeSubscription = stripeSubscriptions.data.find((x) => {
  return x.status === "active" || x.status === "trialing" || x.status === "past_due";
});

But no trial period is passed to Stripe checkout, no trial countdown exists, and no trial emails are sent.

What you'd need to add

1. Install PostHog. The docs already explain how — just do it:

npm install posthog-js
<!-- src/routes/+layout.svelte -->
<script>
  import posthog from 'posthog-js';
  import { browser } from '$app/environment';
  if (browser) {
    posthog.init('your_key', { api_host: 'https://app.posthog.com' });
  }
</script>

Track: signup_completed, pricing_page_viewed, checkout_started, subscription_activated, feature_used.

2. Add subscription checking. Create a helper:

export async function getSubscription(customerId: string) {
  const subscriptions = await stripe.subscriptions.list({
    customer: customerId, status: 'active',
  });
  return subscriptions.data[0];
}

export function getPlanFromPriceId(priceId: string) {
  const plans = { 'price_free': 'free', 'price_xxx': 'pro' };
  return plans[priceId] || 'free';
}

3. Gate one feature. Pick one feature to put behind Pro:

{#if isPro}
  <ProFeature />
{:else}
  <UpgradePrompt feature="advanced_export" />
{/if}

4. Add trial to checkout. One parameter:

const stripeSession = await stripe.checkout.sessions.create({
  // ... existing config
  subscription_data: { trial_period_days: 14 },
});

Full analysis with database schema review: CMSaasStarter PLG Audit

Audit methodology: PLG Skills (open source). You can also run uvx skene-growth analyze . (skene-growth) to scan any codebase for PLG gaps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions