crxpaydocs

React components

Drop-in paywall UI for crxpay subscriptions — six components, one provider, no design system required.

@crxpay/react-paywall is the official React layer for rendering subscription UI on top of @crxpay/sdk. Six components, five hooks, one CSS file. Themed by two CSS variables. Install once, ship a paywall in an afternoon.

Why a separate package

The SDK gives you state and methods (getSubscription, openCheckout, hasEntitlement). It doesn't render anything. @crxpay/react-paywall is the opinionated React UI on top — useful when you want a paywall now without hand-rolling Tailwind cards, Stripe redirects, modal focus traps, theming, and trial countdowns yourself.

Use the SDK directly when you want full control over the UI. Use the components when you want a paywall to ship today.

What ships

Components

ComponentWhat it does
<CrxPayProvider>Root wrapper. Initializes the right transport (extension vs web), exposes context, applies theme as CSS variables. Wrap your tree once.
<UpgradeButton>One-tap CTA that opens Stripe Checkout for a price. Hides itself when the customer is already paying.
<PricingTable>Auto-fetches offerings, renders plan cards, highlights the recommended (yearly) package, computes "Save X%" automatically.
<PaywallModal>Full-screen plan picker with focus trap + Esc + body-scroll lock. Trigger from anywhere via usePaywall().open().
<TrialBadge>Auto-counting "X days left" pill. Hides itself when not trialing. Shifts to a danger style when ≤ 3 days remain.
<ManageSubscriptionLink>Anchor that opens the Stripe Billing Portal. Hides if the customer isn't paying.
<EntitlementGate>Wrap premium UI. Renders children when entitled, fallback otherwise. Default fallback opens the PaywallModal.

Hooks

HookWhat it returns
useCrxPay()Raw context — transport, subscription, theme, paywallOpen state.
useSubscription(){ subscription, loading, isActive, isTrialing, refresh }
useEntitlement(id){ granted, loading } — the feature-gate primitive.
useOfferings(){ offerings, current, loading, refresh } — products + prices.
usePaywall(){ isOpen, open, close } — programmatic modal control.

Quick install

pnpm add @crxpay/react-paywall
import { CrxPayProvider, PricingTable } from '@crxpay/react-paywall';
import '@crxpay/react-paywall/styles.css';

export default function Popup() {
  return (
    <CrxPayProvider apiKey="crxpay_pub_…">
      <h1>Upgrade to Pro</h1>
      <PricingTable />
    </CrxPayProvider>
  );
}

That's the whole integration. The provider auto-detects whether it's running in a Chrome extension or a web app, and routes calls through the right transport without any extra config.

→ Full setup walk-through: Quickstart → Brand customization: Theming → Every prop and hook: API reference

Bundle size

dist/index.esm.js   24 kB   (7.1 kB gz)   — JS
dist/styles.css      9 kB                  — CSS, opt-in

Tree-shakable: components you don't import get dropped. framer-motion is a peer dep — most React apps already have it. If not, add ~30 kB.

What it doesn't do

  • No router. Components don't navigate; they call SDK methods like openCheckout which open Stripe Checkout in a new tab.
  • No state management. Subscription state lives in the SDK's signed cache (extension) or transport memory (web). Components subscribe via the provider.
  • No global CSS reset. Styles are scoped under [data-crxpay-root]. You can keep using Tailwind, shadcn, or anything else side-by-side.
  • No custom design system. If you want to hand-roll your own paywall UI on top of the SDK, that's fine — the components are not required to use crxpay.

Was this page helpful?

Your feedback shapes what we document next.