Skip to main content
NPM Package: @flowglad/react

Overview

The React SDK provides components, hooks, and context for managing billing and subscriptions in your React applications. It works with any React framework and requires a backend server running the Flowglad Server SDK.

Installation

npm install @flowglad/react @flowglad/server
Note: This package requires @flowglad/server to be set up on your backend. See the Server tab or Server SDK documentation for setup instructions.

Quick Start

1. Wrap Your App with FlowgladProvider

import { FlowgladProvider } from '@flowglad/react'

export default function App({ children }) {
  return (
    <FlowgladProvider
      loadBilling={true}
      requestConfig={{
        headers: {
          // Add custom headers if needed
        },
      }}
    >
      {children}
    </FlowgladProvider>
  )
}

2. Use the useBilling Hook

import { useBilling } from '@flowglad/react'

export default function BillingPage() {
  const { checkFeatureAccess, customer, paymentMethods } = useBilling()

  if (!checkFeatureAccess) {
    return <div>Loading...</div>
  }

  if (checkFeatureAccess('premium_feature')) {
    return <div>You have access!</div>
  }

  return <div>Please upgrade</div>
}

Key Features

  • Type-safe Hooks: Access billing data with full TypeScript support
  • React Context: Global state management for billing information
  • Feature Access Control: Check user access to features
  • Checkout Sessions: Create and manage payment flows
  • Subscription Management: Handle subscription lifecycles

API Reference

FlowgladProvider

The main provider component that wraps your application. See FlowgladProvider.tsx for more details

Props

interface FlowgladProviderProps {
  requestConfig?: {
    headers?: Record<string, string>
  }
  baseURL?: string
  loadBilling: boolean
  children: React.ReactNode
}

Example

<FlowgladProvider
  loadBilling={true}
  requestConfig={{
    headers: {
      'X-Custom-Header': 'value',
    },
  }}
>
  {children}
</FlowgladProvider>

useBilling Hook

Access billing data and functions throughout your application.

Load State Helpers

The hook surfaces billing lifecycle metadata you can use to gate UI:
  • loaded: booleantrue once billing data has settled (success or error).
  • loadBilling: boolean – Mirrors the provider prop; if false, billing is intentionally skipped.
  • errors: Error[] | null – Populated when the last fetch failed.
  • reload: () => Promise<void> – Invalidates and refetches billing data.
const { loaded, loadBilling, errors, reload } = useBilling()

if (!loadBilling) return <p>Billing disabled for this view.</p>
if (!loaded) return <p>Loading billing…</p>
if (errors?.length) {
  return (
    <div>
      <p>Unable to load billing information.</p>
      <button onClick={reload}>Try again</button>
    </div>
  )
}

Return Value

interface BillingContext {
  // Customer data
  customer: Customer | null
  
  // Payment methods
  paymentMethods: PaymentMethod[]
  
  // Subscriptions
  currentSubscription: Subscription // The most recently created current subscription for the customer
  currentSubscriptions: Subscription[] // Subscriptions currently active or pending. Length will always be 1 unless your organization settings allow for multiple subscriptions per customer.
  subscriptions: Subscription[] // All subscriptions for the customer
  
  // Purchases
  purchases: Purchase[]
  
  // Invoices
  invoices: Invoice[]
  
  // Catalog and pricing
  pricingModel: PricingModel // The customer's pricing model
  billingPortalUrl: string // URL to the customer's billing portal
  
  // Feature access
  checkFeatureAccess: (featureSlug: string) => boolean
  checkUsageBalance: (
    usageMeterSlug: string,
    options?: { subscriptionId?: string }
  ) => { availableBalance: number } | null
  getProduct: (productSlug: string) => CatalogProduct | null
  getPrice: (priceSlug: string) => Price | null
  
  // Checkout
  createCheckoutSession: (params: CheckoutSessionParams) => Promise<void>
  createAddPaymentMethodCheckoutSession: (
    params: AddPaymentMethodCheckoutParams
  ) => Promise<void>
  createActivateSubscriptionCheckoutSession: (
    params: ActivateSubscriptionCheckoutParams
  ) => Promise<void>
  
  // Subscription management
  cancelSubscription: (params: CancelSubscriptionParams) => Promise<void>
  uncancelSubscription: (params: UncancelSubscriptionParams) => Promise<void>
  
  // Loading states
  isLoading: boolean
  error: Error | null
  
  // Refresh data
  reload: () => Promise<void>
  loadBilling: boolean
  loaded: boolean
  errors: Error[] | null
}

Example Usage

import { useBilling } from '@flowglad/react'

function FeatureGate({ featureSlug, children }) {
  const { checkFeatureAccess, createCheckoutSession } = useBilling()

  if (!checkFeatureAccess) {
    return <div>Loading...</div>
  }

  if (!checkFeatureAccess(featureSlug)) {
    return (
      <div>
        <p>Upgrade to access this feature</p>
        <button
          onClick={() =>
            createCheckoutSession({
              priceSlug: 'pro_plan',
              successUrl: window.location.href,
              cancelUrl: window.location.href,
              autoRedirect: true,
            })
          }
        >
          Upgrade Now
        </button>
      </div>
    )
  }

  return <>{children}</>
}

createCheckoutSession

Create a new checkout session for subscriptions or one-time payments. Checkout sessions for specific prices can be made using either priceSlug (an identifier that you define), or priceId (an identifier Flowglad defines) as a parameter. priceSlug is recommended, so that you don’t need to store any references to Flowglad ids in your application database or environment variables.

Parameters

type FrontendProductCreateCheckoutSessionParams = 
  {
    /**
     * Must specify either priceSlug or priceId
     * Slug is encouraged over Id
     */
    priceSlug?: string
    priceId?: string
    successUrl: string
    cancelUrl: string
    /**
     * Whether to automatically redirect to the hosted checkout page after the session is created
     */
    autoRedirect?: boolean
    quantity?: number
    /**
     * the metadata values to set on the object created by this checkout session
     * - subscription.metadata for subscription prices
     * - purchase.metadata for single payment prices
     */
    outputMetadata?: Record<string, any>
    /**
     * the name value to set on the object created by this checkout session
     * - subscription.name for subscription prices
     * - purchase.name for single payment prices
     */
    outputName?: string
  }

Example

const handleUpgrade = async () => {
  await createCheckoutSession({
    priceSlug: 'price_premium_monthly',
    successUrl: `${window.location.origin}/billing/success`,
    cancelUrl: `${window.location.origin}/billing`,
    autoRedirect: true,
  })
}

createAddPaymentMethodCheckoutSession

Open a Flowglad-hosted flow that collects and stores a new payment method for the signed-in customer.

Parameters

type FrontendCreateAddPaymentMethodCheckoutSessionParams = {
  successUrl: string
  cancelUrl: string
  autoRedirect?: boolean
  outputMetadata?: Record<string, any>
  outputName?: string
  /**
   * When provided, Flowglad sets the newly created payment method
   * as the default for the given subscription.
   */
  targetSubscriptionId?: string
}

Example

await createAddPaymentMethodCheckoutSession({
  successUrl: `${window.location.origin}/billing/payment-methods`,
  cancelUrl: window.location.href,
  autoRedirect: true,
})

createActivateSubscriptionCheckoutSession

Trigger the activation flow for an existing subscription that needs a payment method before billing can start. Activations can happen for subscriptions on trial, or subscriptions where payment is due but has not yet been completed.

Parameters

type FrontendCreateActivateSubscriptionCheckoutSessionParams = {
  /**
   * The subscription to activate.
   */
  targetSubscriptionId: string
  successUrl: string
  cancelUrl: string
  autoRedirect?: boolean
  outputMetadata?: Record<string, any>
  outputName?: string
}

Example

await createActivateSubscriptionCheckoutSession({
  targetSubscriptionId: 'sub_123',
  successUrl: `${window.location.origin}/billing/success`,
  cancelUrl: window.location.href,
  autoRedirect: true,
})

cancelSubscription

Cancel a subscription owned by the current customer and refresh billing data.

Parameters

interface CancelSubscriptionParams {
  id: string
  cancellation:
    | { timing: 'at_end_of_current_billing_period' }
    | { timing: 'immediately' }
}

Example

await cancelSubscription({
  id: 'sub_123',
  cancellation: {
    timing: 'at_end_of_current_billing_period',
  },
})

uncancelSubscription

Reverse a scheduled subscription cancellation. The subscription must be in cancellation_scheduled status. Automatically refreshes billing data on success.

Parameters

interface UncancelSubscriptionParams {
  id: string
}

Example

import { useBilling } from '@flowglad/react'
import { useState } from 'react'

function SubscriptionManager() {
  const { uncancelSubscription, currentSubscription } = useBilling()
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  
  const subscription = currentSubscription
  const isScheduledForCancellation = subscription?.status === 'cancellation_scheduled'
  
  const handleUncancel = async () => {
    if (!subscription?.id) return
    
    setIsLoading(true)
    setError(null)
    
    try {
      await uncancelSubscription({ id: subscription.id })
      // Billing data is automatically refreshed
    } catch (err) {
      setError(
        err instanceof Error
          ? err.message
          : 'Failed to uncancel subscription'
      )
    } finally {
      setIsLoading(false)
    }
  }
  
  if (!isScheduledForCancellation) {
    return null
  }
  
  return (
    <div>
      <p>Your subscription is scheduled to cancel on {new Date(subscription.cancelScheduledAt).toLocaleDateString()}</p>
      <button onClick={handleUncancel} disabled={isLoading}>
        {isLoading ? 'Restoring...' : 'Keep My Subscription'}
      </button>
      {error && <p className="error">{error}</p>}
    </div>
  )
}
Behavior:
  • Operation is idempotent. If the subscription is not scheduled to cancel, the method will silently succeed without uncanceling
  • For paid subscriptions: requires an active payment method for the subscription
  • Automatically refreshes billing data after success

checkFeatureAccess

Check if the current customer has access to a specific feature.

Parameters

  • featureSlug: string - The slug of the feature to check

Returns

  • boolean - true if the customer has access, false otherwise

Example

const hasAdvancedAnalytics = checkFeatureAccess('advanced_analytics')

if (hasAdvancedAnalytics) {
  return <AdvancedAnalytics />
}

return <BasicAnalytics />

checkUsageBalance

Get the remaining balance for a usage meter tied to the customer’s subscriptions.

Parameters

  • usageMeterSlug: string – Slug of the usage meter to inspect.
  • options.subscriptionId?: string – Limit the check to a specific subscription (optional).

Returns

  • { availableBalance: number } | null

Example

const usage = checkUsageBalance('api_calls')
return usage ? (
  <p>{usage.availableBalance} calls remaining</p>
) : (
  <p>No usage meter found.</p>
)

getProduct

Look up a specific product from the catalog that ships with billing data.

Parameters

  • productSlug: string

Returns

  • Product | null

Example

const proPlan = getProduct('pro')
return proPlan ? <h3>{proPlan.name}</h3> : <p>Product unavailable.</p>

getPrice

Look up a price by slug from the live catalog.

Parameters

  • priceSlug: string

Returns

  • Price | null

Example

const monthly = getPrice('pro-monthly')
return monthly ? (
  <span>
    ${(monthly.unitPrice / 100).toFixed(2)}
    {monthly.intervalUnit ? ` / ${monthly.intervalUnit}` : ''}
  </span>
) : (
  <span>Price not available</span>
)

Other billing records

The billing payload also exposes higher-level billing records you can use to build richer UI:
  • customer – The Flowglad customer record (including email, name, IDs).
  • subscriptions – All subscriptions (active and past) returned for the customer.
  • purchases – Historical purchases for the customer.
  • invoices – Invoice records (including status and totals).
  • currentSubscription - The most recently created current subscription for the customer.
  • currentSubscriptions – Subscriptions currently active or pending.
  • paymentMethods – Saved payment methods for the customer.
  • billingPortalUrl – Deep link to Flowglad’s hosted billing portal (if enabled).
  • pricingModel – Resolved pricing model associated with the customer’s plan.
const { customer, subscriptions, paymentMethods, billingPortalUrl } = useBilling()

const activeSub = subscriptions?.find((sub) => sub.status === 'active')
const defaultMethod = paymentMethods?.[0]

Common Patterns

Feature Gating Component

import { useBilling } from '@flowglad/react'

export function FeatureGate({ 
  featureSlug, 
  fallback, 
  children 
}: {
  featureSlug: string
  fallback?: React.ReactNode
  children: React.ReactNode
}) {
  const { checkFeatureAccess } = useBilling()

  if (!checkFeatureAccess) {
    return null
  }

  if (!checkFeatureAccess(featureSlug)) {
    return fallback || <div>Access denied</div>
  }

  return <>{children}</>
}

// Usage
<FeatureGate 
  featureSlug="premium_feature"
  fallback={<UpgradePrompt />}
>
  <PremiumFeature />
</FeatureGate>

Subscription Status Display

import { useBilling } from '@flowglad/react'

export function SubscriptionStatus() {
  const { subscriptions, customer } = useBilling()

  if (!subscriptions || subscriptions.length === 0) {
    return <div>No active subscriptions</div>
  }

  const activeSubscription = subscriptions.find(s => s.status === 'active')

  if (!activeSubscription) {
    return <div>No active subscriptions</div>
  }

  return (
    <div>
      <h3>Current Plan</h3>
      <p>Status: {activeSubscription.status}</p>
      <p>Renews: {new Date(activeSubscription.currentPeriodEnd).toLocaleDateString()}</p>
    </div>
  )
}

Custom Upgrade Button

import { useBilling } from '@flowglad/react'

export function UpgradeButton({ priceSlug }: { priceSlug: string }) {
  const { createCheckoutSession } = useBilling()
  const [isLoading, setIsLoading] = useState(false)

  const handleUpgrade = async () => {
    setIsLoading(true)
    try {
      await createCheckoutSession({
        priceSlug,
        successUrl: `${window.location.origin}/success`,
        cancelUrl: window.location.href,
        autoRedirect: true,
      })
    } catch (error) {
      console.error('Checkout failed:', error)
      setIsLoading(false)
    }
  }

  return (
    <button onClick={handleUpgrade} disabled={isLoading}>
      {isLoading ? 'Loading...' : 'Upgrade'}
    </button>
  )
}

Server Integration

This package requires a backend server running the Flowglad Server SDK. The provider automatically makes requests to /api/flowglad by default. Make sure you have set up the server routes:

Next Steps