Pricing Section
Tiered pricing with billing toggle and Stripe checkout integration.
Features
- • 3-tier pricing (Free, Pro, Ultra)
- • Monthly/yearly billing toggle
- • Automatic savings calculation
- • Popular plan highlighting
Conversion
- • Yearly billing highlighted
- • Clear savings indicators
- • Auth-aware buttons
- • Responsive design
Plan Tiers
Free
$0 forever
Pro ⭐
$20/mo or $200/yr
Ultra
$50/mo or $500/yr
Plans defined in src/lib/subscriptions.ts
Pricing Component
src/components/PricingSection.tsx
1// src/components/PricingSection.tsx2import { auth } from "~/auth";3import { db } from "~/server/db";45export async function PricingSection() {6 const session = await auth();78 const subscription = session?.user?.id9 ? await db.subscription.findUnique({10 where: { userId: session.user.id },11 })12 : null;1314 return (15 <section className="py-24">16 <div className="text-center mb-16">17 <h2 className="text-4xl font-bold">Simple Pricing</h2>18 </div>19 <div className="grid md:grid-cols-3 gap-8">20 {plans.map((plan) => (21 <PricingCard22 key={plan.id}23 plan={plan}24 isLoggedIn={!!session?.user}25 />26 ))}27 </div>28 </section>29 );30}
Pricing Card
1"use client";23import { useState } from "react";4import { createCheckoutSession } from "~/server/actions/stripe";56export function PricingCard({ plan, isLoggedIn }) {7 const [isLoading, setIsLoading] = useState(false);89 const handleSubscribe = async () => {10 if (!isLoggedIn) {11 window.location.href = "/auth/signin";12 return;13 }1415 if (!plan.priceId) {16 window.location.href = "/account";17 return;18 }1920 setIsLoading(true);21 const result = await createCheckoutSession(plan.priceId);22 if (result.url) window.location.href = result.url;23 setIsLoading(false);24 };2526 return (27 <div className={plan.popular ? "border-primary scale-105" : ""}>28 {plan.popular && <div>Most Popular</div>}29 <h3>{plan.name}</h3>30 <p className="text-4xl font-bold">{plan.price}</p>31 <ul>32 {plan.features.map((f, i) => <li key={i}>{f}</li>)}33 </ul>34 <button onClick={handleSubscribe} disabled={isLoading}>35 {plan.current ? "Current Plan" : plan.buttonText}36 </button>37 </div>38 );39}
Testing
Stripe Test Cards
4242 4242 4242 4242— Successful4000 0000 0000 0002— Declined
Best Practices
Conversion
- • Highlight most popular plan
- • Show annual savings
- • Include social proof
- • Clear CTAs
Technical
- • Handle loading states
- • Validate server-side
- • Test checkout flow
- • Monitor conversions
Next: Email Templates
Transactional email designs