BuildBaseBuildBase

Overview

Add subscription plans, checkout, trials, and seat-based pricing to your app.

BuildBase handles subscription billing end-to-end. Use the headless PricingPage component for checkout, and openWorkspaceSettings('subscription') for management. Everything else — Stripe integration, plan switching, trials, invoices — is handled automatically.

Warning

Billing requires setup in the BuildBase dashboard before the SDK components will work:

  1. Connect Stripe — Go to Billing → Credentials and enter your Stripe API keys (use test keys for development)
  2. Create at least one plan — Go to Billing → Plans, create a plan with a name, price, and billing interval
  3. Publish a plan version — Plans use versioning. Create and publish at least one version so it appears on the pricing page
  4. Set up a pricing group — Go to Billing → Pricing Groups. Group your plans so PricingPage knows what to display

Without these steps, PricingPage renders empty plans and selectPlan() fails.

import { PricingPage } from '@buildbase/sdk/react';

function Pricing() {
  return (
    <PricingPage slug="main-pricing">
      {({ loading, error, plans, selectPlan }) => {
        if (loading) return <p>Loading plans...</p>;
        if (error) return <p>{error}</p>;

        return (
          <div className="grid grid-cols-3 gap-4">
            {plans.map((plan) => (
              <div key={plan._id}>
                <h3>{plan.name}</h3>
                <button onClick={() => selectPlan(plan._id, 'monthly', 'usd')}>
                  Choose {plan.name}
                </button>
              </div>
            ))}
          </div>
        );
      }}
    </PricingPage>
  );
}
import { useSaaSAuth } from '@buildbase/sdk/react';

function ManageBilling() {
  const { openWorkspaceSettings } = useSaaSAuth();
  return (
    <button onClick={() => openWorkspaceSettings('subscription')}>
      Manage Subscription
    </button>
  );
}

Before you start

Create plans and pricing groups in the BuildBase dashboard first.

PricingPage

PricingPage is headless: it fetches plan data and handles checkout logic, and you render the UI through a render prop. Both slug (the pricing group to display) and children (the render function) are required.

The render prop receives:

  • plans — published plan versions with pricing, features, limits, and quotas
  • items — subscription items (features, limits, quotas) for building comparison tables
  • loading / error — fetch state
  • selectPlan(planVersionId, interval, currency) — opens the plan dialog for authenticated users, or triggers sign-in first (set redirectBaseUrl on PricingPage for the unauthenticated flow)
  • refetch() — refetch plan data

Optional props: loadingFallback (custom loading UI), errorFallback (custom error UI), and redirectBaseUrl (post-login redirect base for unauthenticated visitors).

openPlanPicker()

Opens the plan selection UI from anywhere in your app — perfect for "Upgrade" buttons inside feature gates or trial banners:

import { useSaaSAuth } from '@buildbase/sdk/react';

function UpgradeButton() {
  const { openPlanPicker } = useSaaSAuth();
  return <button onClick={() => openPlanPicker()}>Upgrade Plan</button>;
}

openWorkspaceSettings('subscription')

Opens the full subscription management screen where users can:

  • View current plan and billing interval
  • Upgrade or downgrade
  • Cancel or resume
  • View invoices and payment history
  • Access the Stripe billing portal

Reading subscription state

Use hooks when you need to display subscription info or conditionally render UI:

import {
  useSubscription,
  useTrialStatus,
  WhenNoSubscription,
  WhenSubscriptionToPlans,
  WhenTrialEnding,
} from '@buildbase/sdk/react';

function Dashboard({ workspaceId }) {
  const { subscription } = useSubscription(workspaceId);
  const { isTrialing, daysRemaining } = useTrialStatus();

  return (
    <>
      {subscription && <p>Plan: {subscription.plan?.name}</p>}
      {isTrialing && <p>{daysRemaining} days left in trial</p>}

      <WhenNoSubscription>
        <p>
          No plan yet. <a href="/pricing">Choose a plan</a>
        </p>
      </WhenNoSubscription>

      <WhenSubscriptionToPlans plans={['starter']}>
        <p>Upgrade to Pro for more features.</p>
      </WhenSubscriptionToPlans>

      <WhenTrialEnding daysThreshold={3}>
        <p>Trial ends soon — subscribe to keep access.</p>
      </WhenTrialEnding>
    </>
  );
}

Hooks

HookWhat it returns
useSubscription(workspaceId)Current plan, status, billing interval
useTrialStatus()isTrialing, daysRemaining
useSeatStatus(workspace)memberCount, includedSeats, canInvite, inviteBlockReason

Conditional components

ComponentRenders when
WhenSubscriptionHas an active subscription
WhenNoSubscriptionNo subscription
WhenSubscriptionToPlansOn a specific plan (pass plans prop)
WhenTrialingIn trial period
WhenNotTrialingNot in trial
WhenTrialEndingTrial ends within daysThreshold days

Server-side

Check subscription status from your backend:

import BuildBase from '@buildbase/sdk';

const bb = BuildBase({ serverUrl, orgId, getSessionId });

const { subscription, plan } = await bb.subscription.get(workspaceId);

if (!subscription || subscription.subscriptionStatus !== 'active') {
  return Response.json({ error: 'Subscription required' }, { status: 403 });
}

Next Steps