SubscriptionCard
Display and manage active subscription with Better Auth Stripe integration
SubscriptionCard
The SubscriptionCard component displays the user's current subscription plan with management actions including access to the Stripe Customer Portal.
v0.3.0 Update: Component now supports auto-fetch mode with Better Auth Stripe plugin. No authClient prop required - just wrap with AuthUIProvider.
Installation
npm install @bettercone/uiFeatures
- ✅ Auto-Fetch Mode - Automatically fetches subscription from Better Auth Stripe plugin
- ✅ Presentational Mode - Pass subscription data manually via
dataprop - ✅ Billing Portal - One-click access to Stripe Customer Portal
- ✅ Status Badges - Visual indicators (active, trialing, past_due, canceled)
- ✅ Organization Support - Auto-detects organization subscriptions
- ✅ Localization - Full i18n support with custom labels
- ✅ Type-Safe - Full TypeScript support
Usage
import { AuthUIProvider, SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function BillingPage() {
return (
<AuthUIProvider authClient={authClient}>
{/* Zero config - auto-fetches subscription */}
<SubscriptionCard />
</AuthUIProvider>
);
}import { SubscriptionCard, type Subscription } from '@bettercone/ui';
export default function BillingPage() {
const subscription: Subscription = {
id: "sub_123",
plan: "pro",
status: "active",
referenceId: userId,
currentPeriodEnd: new Date("2025-12-01"),
seats: 5,
};
return (
<SubscriptionCard
data={subscription}
viewPlansUrl="/pricing"
/>
);
}import { AuthUIProvider, SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function BillingPage() {
return (
<AuthUIProvider authClient={authClient}>
<SubscriptionCard
onBeforeManage={async () => {
// Track analytics, show warning, etc.
console.log('Opening billing portal...');
}}
viewPlansUrl="/pricing?from=billing"
/>
</AuthUIProvider>
);
}import { AuthUIProvider, SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function BillingPage() {
return (
<AuthUIProvider authClient={authClient}>
<SubscriptionCard
localization={{
currentPlan: "Plano Atual",
manageBilling: "Gerenciar Cobrança",
noActiveSubscription: "Nenhuma assinatura ativa",
viewPlans: "Ver Planos",
}}
/>
</AuthUIProvider>
);
}Props
Prop
Type
Component Modes
Mode A: Auto-Fetch (Recommended)
Requires Better Auth Stripe plugin. Component automatically fetches subscription data.
import { AuthUIProvider, SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
<AuthUIProvider authClient={authClient}>
<SubscriptionCard />
</AuthUIProvider>Requirements:
- Better Auth Stripe plugin installed (
@better-auth/stripe) AuthUIProviderwrapping component- User authenticated via Better Auth
Mode B: Presentational
Pass subscription data manually. No Better Auth dependency.
import { SubscriptionCard, type Subscription } from '@bettercone/ui';
const subscription: Subscription = {
id: "sub_123",
plan: "pro",
status: "active",
referenceId: userId,
currentPeriodEnd: new Date("2025-12-01"),
};
<SubscriptionCard data={subscription} />Subscription Types
interface Subscription {
id: string;
plan: string; // Plan ID (e.g., "pro", "enterprise")
referenceId: string; // User or org ID
status: SubscriptionStatus;
currentPeriodStart?: Date;
currentPeriodEnd?: Date;
cancelAtPeriodEnd?: boolean;
cancelAt?: Date;
seats?: number; // Seat limit for team plans
trialStart?: Date;
trialEnd?: Date;
stripeCustomerId?: string;
stripeSubscriptionId?: string;
}
type SubscriptionStatus =
| "incomplete"
| "incomplete_expired"
| "trialing"
| "active"
| "past_due"
| "canceled"
| "unpaid";Subscription Detection
In auto-fetch mode, the component automatically detects subscriptions by:
- Personal Subscriptions: Matches
subscription.referenceIdtouser.id - Organization Subscriptions: Matches
subscription.referenceIdto active organization - Active Status: Filters for
activeortrialingsubscriptions
// Auto-detection logic (internal)
const validReferenceIds = [
session.user.id, // Personal
activeOrganization?.id, // Current org
];
const subscription = subscriptions.find(
sub => validReferenceIds.includes(sub.referenceId) &&
(sub.status === "active" || sub.status === "trialing")
);Status Badges
The component displays different UI based on subscription status:
| Status | Badge Color | Description |
|---|---|---|
active | Green | Subscription is active and paid |
trialing | Blue | In free trial period |
past_due | Yellow | Payment failed, retry in progress |
canceled | Red | Subscription canceled, active until period end |
incomplete | Orange | Requires payment action |
Billing Portal Integration
The "Manage Billing" button opens Stripe's Customer Portal using authClient.subscription.billingPortal().
// Internal implementation
const handleManage = async () => {
const result = await authClient.subscription.billingPortal({
referenceId: subscription.referenceId,
returnUrl: window.location.href,
});
if (result.data?.url) {
window.location.href = result.data.url;
}
};The Stripe Customer Portal must be enabled in your Stripe Dashboard settings.
Examples
Example 1: Billing Dashboard
import { AuthUIProvider } from '@bettercone/ui';
import {
SubscriptionCard,
PaymentMethodCard,
InvoiceHistoryCard
} from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function BillingPage() {
return (
<AuthUIProvider authClient={authClient}>
<div className="container mx-auto py-8 space-y-6">
<h1 className="text-3xl font-bold">Billing</h1>
<div className="grid gap-6 lg:grid-cols-2">
<div className="lg:col-span-2">
<SubscriptionCard />
</div>
<PaymentMethodCard />
<InvoiceHistoryCard />
</div>
</div>
</AuthUIProvider>
);
}Example 2: Organization Billing
import { AuthUIProvider, SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export default function OrgBillingPage() {
return (
<AuthUIProvider authClient={authClient}>
{/* Auto-detects active organization subscription */}
<SubscriptionCard
viewPlansUrl="/org/pricing"
onBeforeManage={async () => {
// Log analytics event
await analytics.track('org_billing_portal_opened');
}}
/>
</AuthUIProvider>
);
}Example 3: Read-Only Display
import { SubscriptionCard, type Subscription } from '@bettercone/ui';
export default function AccountOverview() {
const subscription: Subscription = {
id: "sub_123",
plan: "pro",
status: "active",
referenceId: userId,
currentPeriodEnd: new Date("2025-12-01"),
};
return (
<SubscriptionCard
data={subscription}
showActions={false}
className="border-muted"
/>
);
}Customization
Custom Styling
<SubscriptionCard
className="shadow-lg"
classNames={{
base: "border-2",
header: "bg-primary text-primary-foreground",
content: "bg-muted/50 p-6",
footer: "border-t",
}}
/>Custom Localization
const frenchLocalization = {
currentPlan: "Plan Actuel",
status: "Statut",
renewsOn: "Renouvelle le",
endsOn: "Se termine le",
manageBilling: "Gérer la Facturation",
cancelSubscription: "Annuler l'Abonnement",
noActiveSubscription: "Aucun abonnement actif",
viewPlans: "Voir les Plans",
loading: "Chargement...",
error: "Erreur de chargement",
};
<SubscriptionCard localization={frenchLocalization} />Migration from v0.2.x
Breaking Change: The authClient prop has been removed. Use AuthUIProvider instead.
// Before (v0.2.x)
<SubscriptionCard authClient={authClient} />
// After (v0.3.0) - Auto-fetch
<AuthUIProvider authClient={authClient}>
<SubscriptionCard />
</AuthUIProvider>
// After (v0.3.0) - Presentational
<SubscriptionCard data={subscriptionData} />Related Components
- PaymentMethodCard - Display payment methods
- InvoiceHistoryCard - View past invoices
- TeamBillingCard - Organization billing
- PricingCard - Upgrade plans