Skip to main content

Import

import { CustomerPortal } from "@billingos/sdk";

Basic usage

<CustomerPortal mode="page" />
Renders a complete self-service portal with four tabs:
  • Subscription — Current plan, features, upgrade/downgrade
  • Invoices — Payment history with PDF downloads
  • Payment — Add, remove, and set default payment methods
  • Settings — Update billing information
Customer portal

Props

mode
'drawer' | 'modal' | 'page'
default:"'drawer'"
Display mode:
  • drawer — Slides in from the right
  • modal — Centered overlay
  • page — Full-page inline view
isOpen
boolean
Controls visibility in drawer or modal mode.
onClose
() => void
Called when the user closes the portal (drawer/modal mode).
defaultTab
'subscription' | 'invoices' | 'payment' | 'settings'
default:"'subscription'"
Which tab to show first.
className
string
Custom CSS class name.
customerId
string
Load a specific customer’s portal data.
metadata
Record<string, any>
Custom metadata to pass to the portal session.
onSubscriptionUpdate
(subscription: any) => void
Called when a subscription is upgraded or downgraded.
onSubscriptionCancel
() => void
Called when a subscription is cancelled.
onPaymentMethodAdd
() => void
Called when a new payment method is added.
onPaymentMethodUpdate
() => void
Called when the default payment method changes.
debug
boolean
default:"false"
Enable debug logging.

Examples

Drawer mode (triggered by button)

const [open, setOpen] = useState(false);

<button onClick={() => setOpen(true)}>Manage Billing</button>

<CustomerPortal
  mode="drawer"
  isOpen={open}
  onClose={() => setOpen(false)}
  onSubscriptionCancel={() => {
    toast.info("Subscription cancelled");
    setOpen(false);
  }}
/>

Full page

// app/billing/page.tsx
export default function BillingPage() {
  return (
    <div className="max-w-4xl mx-auto py-8">
      <h1>Billing</h1>
      <CustomerPortal mode="page" defaultTab="subscription" />
    </div>
  );
}
<CustomerPortal
  mode="modal"
  isOpen={open}
  onClose={() => setOpen(false)}
  onSubscriptionUpdate={(sub) => {
    toast.success("Plan updated!");
    router.refresh();
  }}
  onPaymentMethodAdd={() => {
    toast.success("Payment method added");
  }}
/>