Abstract:
This post walks through integrating RevenueCat Web Billing into a plain React/Next.js app. It covers the full setup: creating accounts, configuring products, using the RevenueCat Web SDK, and handling entitlements. Code samples and a recommended context/component hierarchy are provided, along with key gotchas (like the need for both Stripe and RevenueCat accounts, and using the sandbox API key for development). No paywall is implemented—this is a pure integration guide.
Estimated reading time: 5 minutes
Integrating RevenueCat Web Billing in React/Next.js: A Practical Guide
If you want to add subscriptions or one-time purchases to your web app, RevenueCat is a great option. It abstracts away the complexity of payment providers (like Stripe) and gives you a unified API for managing products, purchases, and entitlements. Here’s how I set it up in a plain React/Next.js project, with practical code and best practices.
1. RevenueCat Integration: Sample Code
a. Install the SDK
1
npm install @revenuecat/purchases-js
b. Initialize RevenueCat (in a Context Provider)
Create a context to provide RevenueCat’s customer info and methods throughout your app.
src/contexts/RevenueCatContext.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Filename: src/contexts/RevenueCatContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import Purchases, { CustomerInfo } from '@revenuecat/purchases-js';
const REVENUECAT_API_KEY = process.env.NEXT_PUBLIC_REVENUECAT_API_KEY!; // Use your sandbox key for dev
interface RevenueCatContextValue {
customerInfo: CustomerInfo | null;
isLoading: boolean;
refreshCustomerInfo: () => Promise<void>;
}
const RevenueCatContext = createContext<RevenueCatContextValue | undefined>(undefined);
export const RevenueCatProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [customerInfo, setCustomerInfo] = useState<CustomerInfo | null>(null);
const [isLoading, setIsLoading] = useState(true);
const refreshCustomerInfo = async () => {
setIsLoading(true);
try {
const info = await Purchases.getCustomerInfo();
setCustomerInfo(info);
} catch (e) {
setCustomerInfo(null);
}
setIsLoading(false);
};
useEffect(() => {
Purchases.configure({ apiKey: REVENUECAT_API_KEY });
refreshCustomerInfo();
}, []);
return (
<RevenueCatContext.Provider value=>
{children}
</RevenueCatContext.Provider>
);
};
export const useRevenueCat = () => {
const ctx = useContext(RevenueCatContext);
if (!ctx) throw new Error('useRevenueCat must be used within RevenueCatProvider');
return ctx;
};
Note: The RevenueCat Web SDK is designed to be used client-side only. Do not try to use it in Next.js API routes, getServerSideProps, or any server-side code—it will not work. Always initialize and call it from your React components or client-side hooks.
c. Purchase Flow Example
src/components/PurchaseButton.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Filename: src/components/PurchaseButton.tsx
import React from 'react';
import Purchases from '@revenuecat/purchases-js';
import { useRevenueCat } from '@/contexts/RevenueCatContext';
export const PurchaseButton: React.FC<{ productId: string }> = ({ productId }) => {
const { refreshCustomerInfo } = useRevenueCat();
const handlePurchase = async () => {
try {
await Purchases.purchaseProduct(productId);
await refreshCustomerInfo(); // Refresh entitlements after purchase
alert('Purchase successful!');
} catch (e: any) {
alert('Purchase failed: ' + (e.message || e));
}
};
return (
<button onClick={handlePurchase}>
Purchase
</button>
);
};
d. Checking Entitlements in a Component
src/components/FeatureAccess.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Filename: src/components/FeatureAccess.tsx
import React from 'react';
import { useRevenueCat } from '@/contexts/RevenueCatContext';
export const FeatureAccess: React.FC<{ entitlementId: string }> = ({ entitlementId }) => {
const { customerInfo, isLoading } = useRevenueCat();
if (isLoading) return <div>Loading...</div>;
const hasAccess = !!customerInfo?.entitlements.active[entitlementId]?.isActive;
return (
<div>
{hasAccess ? 'You have access to this feature!' : 'You do not have access.'}
</div>
);
};
2. Component/Context Hierarchy
Recommended Hierarchy:
1
2
3
4
5
6
7
8
9
<App>
<RevenueCatProvider>
<YourAppLayout>
<FeatureAccess entitlementId="pro_feature" />
<PurchaseButton productId="pro_feature_product_id" />
{/* ...other components */}
</YourAppLayout>
</RevenueCatProvider>
</App>
RevenueCatProvider
should wrap your app (ideally at the top level, e.g., in_app.tsx
or your main layout).- All components that need to check entitlements or trigger purchases use the
useRevenueCat
hook. - You can use the context to show/hide premium features, display purchase buttons, etc.
3. Key Points & Best Practices
- API Key: Use the sandbox key for development/testing. Switch to production key for live.
- Entitlements: Set up in RevenueCat dashboard and referenced by
entitlementId
in your code. - Products: Use the product identifier you configured in RevenueCat for purchases.
- Web Billing: RevenueCat handles the checkout UI; you do not need to integrate Stripe directly.
- Customer Info: Always refresh after a purchase to update entitlements.
- Context: Centralizes RevenueCat state and avoids prop drilling.
- The RevenueCat Web SDK is client-side only. Do not attempt to use it in server-side code (API routes, getServerSideProps, etc.)—it will not work.
- You must have both a Stripe account (for payment processing) and a RevenueCat account (for product/entitlement management).
- The RevenueCat Public API Key (rcb_) is for production, you will get errors using it against the Stripe sandbox environment. Use the Sandbox API Key (rcb_sb_…) for development/testing.