Skip to main content

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

useSubscription

Manages subscription lifecycle operations including creation, updating, cancellation, reactivation, and billing portal access. Supports multiple payment providers (Stripe, Polar) with automatic provider detection.

Source: template/hooks/use-subscription.ts

Exported Hooks

HookPurpose
useSubscriptionMutation-based subscription management (create, update, cancel, reactivate, billing portal)
useUserSubscriptionQuery hook to fetch the current user's active subscription
useSubscriptionByIdQuery hook to fetch a specific subscription by ID
useSubscriptionManagerHigher-level manager with optimistic update support

useSubscription

Return Values

const {
// Mutations (TanStack Query UseMutationResult objects)
createSubscription, // UseMutation<SubscriptionData, Error, CreateSubscriptionRequest>
updateSubscription, // UseMutation<SubscriptionData, Error, UpdateSubscriptionRequest>
updateSubscriptionById, // UseMutation<SubscriptionData, Error, UpdateSubscriptionRequest>
cancelSubscription, // UseMutation<SubscriptionData, Error, CancelSubscriptionRequest>
cancelSubscriptionById, // UseMutation<SubscriptionData, Error, CancelSubscriptionRequest>
reactivateSubscription, // UseMutation<SubscriptionData, Error, ReactivateSubscriptionRequest>
createBillingPortalSession,// UseMutation<BillingPortalResponse, Error, void>

// Loading states (boolean)
isCreating, isUpdating, isUpdatingById,
isCancelling, isCancellingById, isReactivating,

// Error states (Error | null)
createError, updateError, updateByIdError,
cancelError, cancelByIdError, reactivateError,

// Success states (boolean)
isCreateSuccess, isUpdateSuccess, isUpdateByIdSuccess,
isCancelSuccess, isCancelByIdSuccess, isReactivateSuccess,

// Billing portal states
isCreateBillingPortalSessionPending,
isCreateBillingPortalSessionSuccess,
isCreateBillingPortalSessionError,

// Reset functions
resetCreate, resetUpdate, resetUpdateById,
resetCancel, resetCancelById, resetReactivate,
} = useSubscription();

Types

interface SubscriptionData {
id: string;
status: string;
currentPeriodStart: number;
currentPeriodEnd: number;
cancelAtPeriodEnd: boolean;
metadata: Record<string, any>;
}

interface CreateSubscriptionRequest {
priceId: string;
paymentMethodId: string;
trialPeriodDays?: number;
}

interface UpdateSubscriptionRequest {
subscriptionId: string;
priceId?: string;
cancelAtPeriodEnd?: boolean;
billingInterval?: string;
planId?: string;
}

interface CancelSubscriptionRequest {
subscriptionId: string;
cancelAtPeriodEnd: boolean;
}

interface ReactivateSubscriptionRequest {
subscriptionId: string;
}

Usage: Creating a Subscription

function CheckoutButton({ priceId, paymentMethodId }) {
const { createSubscription, isCreating, createError } = useSubscription();

const handleSubscribe = () => {
createSubscription.mutate({
priceId,
paymentMethodId,
trialPeriodDays: 14,
});
};

return (
<button onClick={handleSubscribe} disabled={isCreating}>
{isCreating ? 'Processing...' : 'Subscribe'}
</button>
);
}

Usage: Billing Portal

function ManageBillingButton() {
const { createBillingPortalSession, isCreateBillingPortalSessionPending } = useSubscription();

const handleManageBilling = async () => {
createBillingPortalSession.mutate(undefined, {
onSuccess: (data) => {
window.location.href = data.data.url;
},
});
};

return (
<button onClick={handleManageBilling} disabled={isCreateBillingPortalSessionPending}>
Manage Billing
</button>
);
}

useUserSubscription

Fetches the current user's active subscription data. Returns a standard TanStack Query result.

function SubscriptionStatus() {
const { data: subscription, isLoading, isError } = useUserSubscription();

if (isLoading) return <Skeleton />;
if (!subscription) return <p>No active subscription</p>;

return (
<div>
<p>Status: {subscription.status}</p>
<p>Renews: {new Date(subscription.currentPeriodEnd).toLocaleDateString()}</p>
</div>
);
}

Cache configuration: staleTime: 5 min, gcTime: 10 min. Query key: ['user-subscription'].

useSubscriptionById

Fetches a specific subscription by its ID. The query is disabled when subscriptionId is empty.

const { data, isLoading } = useSubscriptionById('sub_abc123');

Query key: ['subscription', subscriptionId].

useSubscriptionManager

Wraps useSubscription with optimistic update support for subscription creation. On mutate, the cache is immediately updated with a temporary subscription object; on error, the previous value is restored.

function SubscriptionFlow() {
const { createSubscription, isCreating, createError } = useSubscriptionManager();

const handleCreate = (request: CreateSubscriptionRequest) => {
createSubscription.mutate(request);
};

// UI updates instantly via optimistic cache, then reconciles with server
}

Payment Provider Awareness

The hook automatically detects the active payment provider using useSelectedCheckoutProvider and usePaymentProvider. The billing portal endpoint adapts accordingly:

  • Stripe: POST /api/stripe/subscription/portal
  • Polar: POST /api/polar/subscription/portal

Cache Invalidation

All mutations invalidate the following query keys on success:

Query KeyPurpose
['subscriptions']List of all subscriptions
['user-subscription']Current user's subscription
['billing']Billing-related data

Error Handling

Errors are captured per-mutation. Access them via createError, updateError, etc. Each mutation also exposes a reset function to clear error state:

const { createError, resetCreate } = useSubscription();

if (createError) {
return (
<div>
<p>Error: {createError.message}</p>
<button onClick={resetCreate}>Try Again</button>
</div>
);
}