ComponentsPricing
PricingCard
Pricing plan card with auto-checkout via Better Auth Stripe
PricingCard
The PricingCard component displays a pricing plan with automatic Stripe Checkout integration via Better Auth.
v0.3.0 Update: Component now auto-handles checkout with Better Auth Stripe plugin when stripePriceId fields are provided.
Installation
npm install @bettercone/uiFeatures
- ✅ Auto-Checkout - Automatic Stripe Checkout when
stripePriceIdfields provided - ✅ Custom Callback - Fallback to
onSubscribewhen no Stripe IDs - ✅ Billing Interval - Switch between monthly/yearly pricing
- ✅ Popular Badge - Highlight recommended plans
- ✅ Feature Lists - Display plan features with icons
- ✅ Localization - Full i18n support
Usage
import { AuthUIProvider, PricingCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function PricingPage() {
return (
<AuthUIProvider authClient={authClient}>
<PricingCard
plan={{
id: "pro",
name: "Pro Plan",
description: "For growing teams",
priceMonthly: 29,
priceYearly: 290,
features: [
"Unlimited projects",
"Advanced analytics",
"Priority support",
],
popular: true,
// Auto-checkout enabled with Stripe IDs
stripePriceIdMonthly: "price_monthly_xxx",
stripePriceIdYearly: "price_yearly_xxx",
}}
billingInterval="monthly"
/>
</AuthUIProvider>
);
}import { PricingCard } from '@bettercone/ui';
export default function PricingPage() {
const handleSubscribe = async (planId: string, interval: string) => {
// Custom checkout flow
await yourCheckoutFunction(planId, interval);
};
return (
<PricingCard
plan={{
id: "pro",
name: "Pro Plan",
priceMonthly: 29,
priceYearly: 290,
features: ["Feature 1", "Feature 2"],
// No stripePriceId - uses onSubscribe callback
}}
billingInterval="monthly"
onSubscribe={handleSubscribe}
/>
);
}import { PricingCard } from '@bettercone/ui';
import { useState } from 'react';
export default function PricingPage() {
const [interval, setInterval] = useState<"monthly" | "yearly">("monthly");
return (
<>
<div className="flex gap-2 mb-8">
<button onClick={() => setInterval("monthly")}>Monthly</button>
<button onClick={() => setInterval("yearly")}>Yearly</button>
</div>
<PricingCard
plan={planData}
billingInterval={interval}
/>
</>
);
}Props
Prop
Type
Pricing Plan Types
interface PricingPlan {
id: string; // Plan ID (must match Better Auth config)
name: string;
description?: string;
priceMonthly: number; // Price in dollars
priceYearly: number;
currency?: string; // Default: "USD"
features: string[];
popular?: boolean;
// Auto-checkout (optional)
stripePriceIdMonthly?: string;
stripePriceIdYearly?: string;
// Limits
maxUsers?: number;
maxProjects?: number;
storageGb?: number;
}Auto-Checkout Flow
When stripePriceId fields are provided, the component automatically:
- Detects Better Auth Stripe plugin via
authClient.subscription.upgrade - Calls
authClient.subscription.upgrade()with selected plan and interval - Redirects user to Stripe Checkout
- Returns to
successUrlafter payment
// Internal implementation
const handleCheckout = async () => {
const stripePriceId = billingInterval === "monthly"
? plan.stripePriceIdMonthly
: plan.stripePriceIdYearly;
const result = await authClient.subscription.upgrade({
planId: plan.id,
stripePriceId,
successUrl: successUrl || `${window.location.origin}/billing?success=true`,
cancelUrl: cancelUrl || window.location.href,
});
if (result.data?.url) {
window.location.href = result.data.url;
}
};Examples
Example 1: Pricing Grid
import { AuthUIProvider, PricingCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function PricingPage() {
const plans = [
{
id: "starter",
name: "Starter",
priceMonthly: 9,
priceYearly: 90,
features: ["5 projects", "Basic support"],
stripePriceIdMonthly: "price_starter_monthly",
stripePriceIdYearly: "price_starter_yearly",
},
{
id: "pro",
name: "Pro",
priceMonthly: 29,
priceYearly: 290,
features: ["Unlimited projects", "Priority support"],
popular: true,
stripePriceIdMonthly: "price_pro_monthly",
stripePriceIdYearly: "price_pro_yearly",
},
];
return (
<AuthUIProvider authClient={authClient}>
<div className="grid md:grid-cols-2 gap-8">
{plans.map(plan => (
<PricingCard key={plan.id} plan={plan} billingInterval="monthly" />
))}
</div>
</AuthUIProvider>
);
}Example 2: With Analytics
<PricingCard
plan={plan}
billingInterval={interval}
onBeforeCheckout={async () => {
await analytics.track('checkout_started', {
plan: plan.id,
interval,
});
}}
successUrl="/billing?success=true&plan=${plan.id}"
/>Example 3: Custom Styling
<PricingCard
plan={plan}
billingInterval="yearly"
className="hover:shadow-xl transition-shadow"
classNames={{
base: "border-2 border-primary",
header: "bg-gradient-to-r from-blue-500 to-purple-500 text-white",
price: "text-4xl font-bold",
features: "space-y-3",
button: "w-full bg-primary hover:bg-primary/90",
}}
/>Migration from v0.2.x
// Before (v0.2.x)
<PricingCard
plan={planData}
billingInterval="monthly"
isLoading={loading}
onSubscribe={handleSubscribe}
/>
// After (v0.3.0) - Auto-checkout
<AuthUIProvider authClient={authClient}>
<PricingCard
plan={{
...planData,
stripePriceIdMonthly: "price_xxx",
stripePriceIdYearly: "price_yyy",
}}
billingInterval="monthly"
/>
</AuthUIProvider>
// After (v0.3.0) - Custom callback (no breaking changes)
<PricingCard
plan={planData}
billingInterval="monthly"
onSubscribe={handleSubscribe}
/>Related Components
- SubscriptionCard - Display current subscription
- PricingDashboard - Complete pricing page
- BillingDashboard - Billing management