Quickstart
Wire @crxpay/react-paywall into a Chrome extension popup in under five minutes.
This walks you through adding a paywall to a Chrome extension popup using the
components. The same instructions apply to web apps — you'll just pass an
extra customerEmail prop on the provider.
1. Install
pnpm add @crxpay/sdk @crxpay/react-paywall
You'll need both: @crxpay/sdk runs in your background service worker; the
components run in the popup and proxy through it.
2. Configure the SDK in your background
import { CrxPay } from '@crxpay/sdk/background';
CrxPay.configure({
apiKey: 'crxpay_pub_…', // from the dashboard's API keys page
testMode: 'auto', // detects unpacked extensions automatically
});
That's all the background needs. The SDK handles the signed cache, the FSM, and message routing.
3. Wrap your popup with the provider
import { createRoot } from 'react-dom/client';
import { CrxPayProvider } from '@crxpay/react-paywall';
import '@crxpay/react-paywall/styles.css';
import { App } from './App';
createRoot(document.getElementById('root')!).render(
<CrxPayProvider apiKey="crxpay_pub_…">
<App />
</CrxPayProvider>,
);
The apiKey is the same crxpay_pub_* you use in the background. The
provider auto-detects MV3 (chrome.runtime?.id is set) and proxies all
calls through the background — no CORS, no extra plumbing.
4. Render a paywall
The simplest possible paywall:
import { PricingTable } from '@crxpay/react-paywall';
export function App() {
return (
<div className="popup">
<h1>Upgrade to Pro</h1>
<PricingTable />
</div>
);
}
<PricingTable> fetches the active offering from the dashboard, renders
plan cards (highlighting the yearly one as "Best value"), and opens Stripe
Checkout when clicked. No plumbing required.
5. Gate premium features
Wrap any UI you want to lock behind a paid plan:
import { EntitlementGate } from '@crxpay/react-paywall';
<EntitlementGate entitlement="pro">
<ProOnlyFeature />
</EntitlementGate>
The default fallback shows a small "Upgrade to unlock" card that opens a
<PaywallModal>. Pass your own fallback prop to
customize.
6. Add a manage-subscription link
Once a customer is paying, give them a way to manage their subscription:
import { ManageSubscriptionLink } from '@crxpay/react-paywall';
<footer>
<ManageSubscriptionLink />
</footer>
Hides itself when there's no active subscription. Opens Stripe's hosted Billing Portal when clicked.
7. Apply your brand
Pass a theme to the provider — every component picks it up via CSS variables:
<CrxPayProvider
apiKey="crxpay_pub_…"
theme={{
accent: '#10B981', // emerald — your CTA + selected-plan accent
text: '#0F2A21', // optional override
radius: '20px', // optional — softer cards
}}
>
…
</CrxPayProvider>
→ Full theme reference: Theming
8. Mount the paywall modal once near your root
The modal lives in a portal at document.body, so any component anywhere can
trigger it via the usePaywall() hook:
import { CrxPayProvider, PaywallModal } from '@crxpay/react-paywall';
<CrxPayProvider apiKey="…">
<App />
<PaywallModal />
</CrxPayProvider>
Then in any descendant:
import { usePaywall } from '@crxpay/react-paywall';
function UpgradeBanner() {
const { open } = usePaywall();
return <button onClick={open}>Upgrade to unlock</button>;
}
You're done
The paywall is now live. When a customer completes Stripe Checkout, the
SDK polls the API, updates its signed cache, and broadcasts a paid event to
every component using your provider — including <EntitlementGate>, which
re-renders to show the gated content.
→ See every prop in the API reference.
Was this page helpful?
Your feedback shapes what we document next.