How the Subscription and Premium Feature Gating System Works in Developer Roadmap
The developer-roadmap repository implements premium feature gating through a client-side React Query hook that polls a /v1-billing-details endpoint to determine subscription status, conditionally rendering UI components based on the isPaidUser boolean while managing upgrade flows via nanostores and Stripe checkout sessions.
The subscription and premium feature gating system in the developer-roadmap repository controls access to advanced features like AI-powered roadmap generation and personalized learning paths. This React-based implementation relies on a coordinated architecture between a billing status API, React Query hooks for state management, and conditional component rendering to enforce access control entirely on the client side.
Architecture Overview of the Premium Gating System
The system operates through four coordinated layers: a server-side billing endpoint that exposes subscription status, a React Query hook that caches and polls this data, UI components that gate features based on this state, and a nanostore-backed modal system that handles upgrade flows.
Server-Side Billing Status Endpoint
The /v1-billing-details API
The backend exposes a REST endpoint at /v1-billing-details that returns a JSON payload containing the user's subscription metadata. This includes the status field (indicating whether the subscription is "active"), along with priceId and interval information necessary for plan management and upgrade flows.
Client-Side Subscription Detection with React Query
The useIsPaidUser Hook
Located in src/queries/billing.ts, the useIsPaidUser hook encapsulates the subscription detection logic. It uses React Query to poll the billing endpoint and derive a boolean flag indicating premium access.
export function useIsPaidUser() {
const { data, isLoading } = useQuery(
{ queryKey: ['billing-details'], queryFn: () => httpGet('/v1-billing-details'), enabled: !!isLoggedIn() },
queryClient,
);
return { isPaidUser: data?.status === 'active', isLoading };
}
The hook only executes when isLoggedIn() returns true, preventing unnecessary API calls for anonymous users. It returns isPaidUser: true only when the subscription status equals "active", providing a reactive boolean that components can use for conditional rendering.
Implementing Feature Gates in React Components
Conditional Rendering Based on Subscription Status
UI components throughout the application import useIsPaidUser to determine whether to render premium features or upgrade prompts. This pattern appears in over 40 locations across the codebase, including roadmap generation, AI tutoring, and content creation features.
For example, in src/components/TopNavDropdowns/UpgradeProButton.tsx, the component conditionally renders based on subscription status:
const { isPaidUser, isLoading } = useIsPaidUser();
return isPaidUser ? null : <UpgradeButton disabled={isLoading} />;
This pattern ensures that premium UI elements are completely removed from the DOM for active subscribers, while non-subscribers see targeted upgrade prompts that trigger the purchase flow.
Managing the Upgrade Flow
Modal State Management with Nanostores
The application uses a lightweight nanostores store defined in src/stores/subscription.ts to manage the upgrade modal's visibility state. This store provides a global boolean isUpgradeModalOpen and helper functions showUpgradeModal() and hideUpgradeModal() that any component can import to trigger the purchase interface without prop drilling.
Stripe Checkout Integration
The premium landing page at src/components/Premium/PremiumPage.tsx handles the actual subscription creation through a React Query mutation. When a user selects a plan, the component calls the /v1-create-subscription-checkout-session endpoint and redirects to Stripe's hosted checkout page.
import { useMutation } from '@tanstack/react-query';
import { httpPost } from '../../lib/query-http';
const { mutate: startCheckout } = useMutation(
(priceId: string) => httpPost<{ checkoutUrl: string }>('/v1-create-subscription-checkout-session', { priceId }),
{
onSuccess: ({ checkoutUrl }) => (window.location.href = checkoutUrl),
onError: (e) => console.error('checkout error', e),
},
);
Plan Switching for Existing Subscribers
For users with existing subscriptions who wish to change plans, the application renders an UpdatePlanConfirmation modal defined in src/components/Billing/UpdatePlanConfirmation.tsx. This component calls the /v1-update-subscription-plan endpoint to modify the existing Stripe subscription without requiring the user to re-enter payment details.
Post-Checkout Subscription Activation
After the user completes payment on Stripe's hosted page and returns to the application, the useIsPaidUser hook automatically refreshes the billing details cache. This reactive update instantly unlocks premium UI components across the application without requiring a page refresh, as React Query's background refetch detects the updated subscription status.
Summary
- The subscription and premium feature gating system relies on a client-side architecture using React Query to poll
/v1-billing-detailsand determine access rights. - The
useIsPaidUserhook insrc/queries/billing.tsprovides a reactive boolean flag that components use to conditionally render premium features or upgrade prompts. - Over 40 UI components implement feature gating by checking
isPaidUser, ensuring premium functionality is only accessible to active subscribers. - The upgrade flow uses nanostores for modal state management and Stripe-hosted checkout via
/v1-create-subscription-checkout-sessionto handle payments securely. - Plan switching for existing subscribers is handled through
/v1-update-subscription-planwithout requiring new payment information.
Frequently Asked Questions
How does the developer-roadmap repository check if a user has an active subscription?
The repository uses the useIsPaidUser hook defined in src/queries/billing.ts. This React Query hook polls the /v1-billing-details endpoint and returns true only when the subscription status field equals "active". Components throughout the application use this boolean to gate access to premium features.
Is the premium feature gating implemented on the server or client side?
The gating is enforced entirely on the client side. While the server provides the billing status via the /v1-billing-details endpoint, the decision to render or hide premium UI components is made by React components checking the isPaidUser flag. This architecture relies on the server endpoint returning accurate subscription data while the UI handles the conditional rendering logic.
How does the upgrade flow work when a user wants to purchase a premium plan?
The upgrade flow begins when a component calls showUpgradeModal() from the nanostores subscription store. The PremiumPage component in src/components/Premium/PremiumPage.tsx then renders pricing options, and selecting a plan triggers a mutation to /v1-create-subscription-checkout-session. Upon success, the user is redirected to Stripe's hosted checkout page. After payment completion, returning to the application triggers a refresh of the billing details, instantly unlocking premium features.
Can existing subscribers change their subscription plan without canceling?
Yes, existing subscribers can switch plans through the UpdatePlanConfirmation modal component in src/components/Billing/UpdatePlanConfirmation.tsx. When a user selects a different plan, the application calls the /v1-update-subscription-plan endpoint to modify the existing Stripe subscription. This process updates the subscription immediately without requiring the user to re-enter payment details or cancel their current plan first.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →