Back to Blog
·10 min

How to Build a Subscription Billing Dashboard

Subscription billing is the lifeblood of SaaS businesses. Managing plans, processing payments, handling upgrades and downgrades, and tracking churn requires a robust billing system.

In this guide, we will build a subscription management dashboard with plan creation, customer management, billing automation, and revenue analytics.

Database Schema

model Plan {
  id         String         @id @default(cuid())
  name       String
  slug       String         @unique
  description String?
  price      Decimal
  interval   String         @default("month")
  features   String[]
  popular    Boolean        @default(false)
  active     Boolean        @default(true)
  subscriptions Subscription[]
}

model Subscription {
  id         String   @id @default(cuid())
  planId     String
  plan       Plan     @relation(fields: [planId], references: [id])
  customerId String
  status     String   @default("active")
  currentPeriodStart DateTime
  currentPeriodEnd   DateTime
  cancelAtPeriodEnd  Boolean  @default(false)
  createdAt  DateTime @default(now())
}

Plan Comparison Component

function PlanCards({ plans, onSelect }: { plans: Plan[]; onSelect: (plan: Plan) => void }) {
  return (
    <div className="grid grid-cols-3 gap-4">
      {plans.map(plan => (
        <div key={plan.id} className={`relative rounded-xl p-6 border-2 ${
          plan.popular ? 'border-brand-500 bg-gray-900' : 'border-gray-700 bg-gray-900'
        }`}>
          {plan.popular && (
            <span className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-brand-600 px-3 py-1 text-xs text-white">
              Most Popular
            </span>
          )}
          <h3 className="text-lg font-bold text-white">{plan.name}</h3>
          <p className="mt-1 text-sm text-gray-400">{plan.description}</p>
          <p className="mt-4">
            <span className="text-3xl font-bold text-white">${plan.price}</span>
            <span className="text-sm text-gray-400">/{plan.interval}</span>
          </p>
          <ul className="mt-4 space-y-2">
            {plan.features.map((f, i) => (
              <li key={i} className="flex items-center gap-2 text-sm text-gray-300">
                <CheckCircle className="h-4 w-4 text-green-400" />
                {f}
              </li>
            ))}
          </ul>
          <button onClick={() => onSelect(plan)}
            className="mt-6 w-full rounded-lg bg-brand-600 py-2 text-sm font-semibold text-white hover:bg-brand-700"
          >
            {plan.price === 0 ? 'Get Started' : 'Subscribe'}
          </button>
        </div>
      ))}
    </div>
  )
}

Revenue Analytics

async function getRevenueMetrics() {
  const activeSubs = await prisma.subscription.count({ where: { status: 'active' } })
  const mrr = await prisma.subscription.aggregate({
    where: { status: 'active' },
    _sum: { plan: { price: true } },
  })
  const churned = await prisma.subscription.count({
    where: { status: 'cancelled', updatedAt: { gte: subDays(new Date(), 30) } }
  })
  return { mrr: mrr._sum.price || 0, activeSubs, churned, churnRate: churned / activeSubs }
}

Cross-Selling: Launch Your Subscription Platform

Our Subscription Manager template provides all of this out of the box — plan creation, billing management, payment history, churn analytics, dunning emails, and revenue reports. It integrates with Stripe for payment processing.

Pair with Invoice Manager for professional invoicing, or Customer Success Platform to track health scores and reduce churn. Together they form a complete billing and success platform.

Ready to Build?

Get started with our production-ready starter kits and ship your project faster.

Browse Starter Kits