Official React SDK for SubLoop's payment analytics API. Includes React hooks and pre-built components for easy integration.
npm install subloop-react
import React from 'react';
import { SubLoopProvider, AnalyticsDashboard } from 'subloop-react';
function App() {
return (
<SubLoopProvider apiKey="sk_your_api_key_here">
<div className="app">
<h1>My Revenue Dashboard</h1>
<AnalyticsDashboard autoRefresh={true} />
</div>
</SubLoopProvider>
);
}
export default App;
import React from 'react';
import { useSubLoop, useAnalytics, usePaymentTracking } from 'subloop-react';
function MyComponent() {
const client = useSubLoop('sk_your_api_key_here');
const { data, loading, error, refresh } = useAnalytics(client, { autoRefresh: true });
const { trackSuccessfulPayment } = usePaymentTracking(client);
const handlePayment = async () => {
try {
await trackSuccessfulPayment('cust_001', 29.99, 'USD', 'sub_001');
refresh(); // Refresh analytics after payment
} catch (error) {
console.error('Payment tracking failed:', error);
}
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Revenue: ${data?.overview?.total_revenue_30d}</h2>
<button onClick={handlePayment}>Track Payment</button>
</div>
);
}
Provides SubLoop client context to child components.
<SubLoopProvider apiKey="sk_your_api_key" baseUrl="https://api.getsubloop.com">
<YourApp />
</SubLoopProvider>
Props:
apiKey (string, required): Your SubLoop API keybaseUrl (string, optional): API base URLchildren (ReactNode): Child componentsComplete analytics dashboard with overview cards, MRR growth, and recent payments.
<AnalyticsDashboard
autoRefresh={true}
refreshInterval={30000}
showMRRGrowth={true}
showRecentPayments={true}
recentPaymentsLimit={5}
/>
Props:
autoRefresh (boolean): Enable auto-refreshrefreshInterval (number): Refresh interval in millisecondsshowMRRGrowth (boolean): Show MRR growth componentshowRecentPayments (boolean): Show recent payments componentrecentPaymentsLimit (number): Number of recent payments to showOverview metrics cards showing key revenue metrics.
<AnalyticsOverview autoRefresh={true} refreshInterval={30000} />
MRR growth component with current/previous MRR and growth percentage.
<MRRGrowth autoRefresh={true} refreshInterval={60000} />
List of recent payment transactions.
<RecentPayments
limit={10}
autoRefresh={true}
refreshInterval={30000}
/>
Reusable metric card component.
<OverviewCard
title="Monthly Revenue"
value="$12,345"
icon="💰"
color="#3B82F6"
/>
Main hook for creating SubLoop client instance.
const client = useSubLoop(apiKey, baseUrl);
Hook for fetching complete analytics data.
const { data, loading, error, refresh } = useAnalytics(client, {
autoRefresh: true,
refreshInterval: 30000
});
Returns:
data: Analytics data object with overview, MRR, and recent paymentsloading: Loading stateerror: Error object if request failedrefresh: Function to manually refresh dataHook for tracking payment events.
const {
trackPayment,
trackSuccessfulPayment,
trackFailedPayment,
loading,
error,
lastPayment
} = usePaymentTracking(client);
Returns:
trackPayment(data): Track custom payment eventtrackSuccessfulPayment(customerId, amount, currency, subscriptionId, metadata): Track successful paymenttrackFailedPayment(customerId, amount, currency, subscriptionId, metadata): Track failed paymentloading: Loading stateerror: Error object if request failedlastPayment: Last tracked payment dataHook for fetching revenue data.
const { data, loading, error, refresh } = useRevenue(client, '30days', {
startDate: '2025-06-01',
endDate: '2025-07-31',
autoRefresh: true,
refreshInterval: 60000
});
Hook for fetching MRR data.
const { data, loading, error, refresh } = useMRR(client, {
autoRefresh: true,
refreshInterval: 60000
});
Hook for fetching payments list.
const { data, loading, error, refresh } = usePayments(client, { page: 1 }, {
autoRefresh: true,
refreshInterval: 30000
});
import React from 'react';
import {
SubLoopProvider,
AnalyticsDashboard,
useSubLoopContext,
usePaymentTracking
} from 'subloop-react';
function PaymentForm() {
const client = useSubLoopContext();
const { trackSuccessfulPayment, loading } = usePaymentTracking(client);
const [formData, setFormData] = React.useState({
customerId: '',
amount: '',
currency: 'USD'
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
await trackSuccessfulPayment(
formData.customerId,
parseFloat(formData.amount),
formData.currency
);
alert('Payment tracked successfully!');
setFormData({ customerId: '', amount: '', currency: 'USD' });
} catch (error) {
alert('Error tracking payment: ' + error.message);
}
};
return (
<form onSubmit={handleSubmit} style={{ marginBottom: '20px' }}>
<h3>Track New Payment</h3>
<input
type="text"
placeholder="Customer ID"
value={formData.customerId}
onChange={(e) => setFormData({...formData, customerId: e.target.value})}
required
/>
<input
type="number"
placeholder="Amount"
value={formData.amount}
onChange={(e) => setFormData({...formData, amount: e.target.value})}
required
/>
<select
value={formData.currency}
onChange={(e) => setFormData({...formData, currency: e.target.value})}
>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="GBP">GBP</option>
</select>
<button type="submit" disabled={loading}>
{loading ? 'Tracking...' : 'Track Payment'}
</button>
</form>
);
}
function App() {
return (
<SubLoopProvider apiKey={process.env.REACT_APP_SUBLOOP_API_KEY}>
<div style={{ padding: '20px' }}>
<h1>Revenue Analytics Dashboard</h1>
<PaymentForm />
<AnalyticsDashboard autoRefresh={true} refreshInterval={30000} />
</div>
</SubLoopProvider>
);
}
export default App;
import React from 'react';
import { useRevenue, useMRR, useSubLoopContext } from 'subloop-react';
function CustomAnalytics() {
const client = useSubLoopContext();
const { data: revenue30d } = useRevenue(client, '30days');
const { data: revenue7d } = useRevenue(client, '7days');
const { data: mrr } = useMRR(client);
return (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '20px' }}>
<div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h3>7-Day Revenue</h3>
<p style={{ fontSize: '24px', fontWeight: 'bold' }}>
${revenue7d?.total_revenue?.toLocaleString() || '0'}
</p>
</div>
<div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h3>30-Day Revenue</h3>
<p style={{ fontSize: '24px', fontWeight: 'bold' }}>
${revenue30d?.total_revenue?.toLocaleString() || '0'}
</p>
</div>
<div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h3>MRR Growth</h3>
<p style={{ fontSize: '24px', fontWeight: 'bold' }}>
{mrr?.growth_percentage?.toFixed(1) || '0'}%
</p>
<p style={{ fontSize: '14px', color: '#666' }}>
Current: ${mrr?.mrr?.toLocaleString() || '0'}
</p>
</div>
</div>
);
}
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
import { usePaymentTracking, useSubLoopContext } from 'subloop-react';
const stripePromise = loadStripe('pk_test_your_stripe_key');
function CheckoutForm({ customerId, amount, onSuccess }) {
const stripe = useStripe();
const elements = useElements();
const client = useSubLoopContext();
const { trackSuccessfulPayment, trackFailedPayment } = usePaymentTracking(client);
const [loading, setLoading] = React.useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) return;
setLoading(true);
try {
// Create payment intent on your backend
const { client_secret } = await createPaymentIntent(amount);
// Confirm payment with Stripe
const { error, paymentIntent } = await stripe.confirmCardPayment(client_secret, {
payment_method: {
card: elements.getElement(CardElement),
}
});
if (error) {
// Track failed payment in SubLoop
await trackFailedPayment(customerId, amount, 'USD', null, {
stripe_error: error.message,
payment_intent_id: paymentIntent?.id
});
alert('Payment failed: ' + error.message);
} else {
// Track successful payment in SubLoop
await trackSuccessfulPayment(customerId, amount, 'USD', null, {
stripe_payment_intent_id: paymentIntent.id,
stripe_payment_method_id: paymentIntent.payment_method
});
onSuccess(paymentIntent);
}
} catch (error) {
console.error('Payment error:', error);
await trackFailedPayment(customerId, amount, 'USD', null, {
error: error.message
});
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<CardElement />
<button type="submit" disabled={!stripe || loading}>
{loading ? 'Processing...' : `Pay $${amount}`}
</button>
</form>
);
}
function PaymentPage() {
return (
<Elements stripe={stripePromise}>
<CheckoutForm
customerId="cust_001"
amount={29.99}
onSuccess={(paymentIntent) => {
alert('Payment successful!');
}}
/>
</Elements>
);
}
import React from 'react';
import { useAnalytics, useSubLoopContext } from 'subloop-react';
function RealTimeDashboard() {
const client = useSubLoopContext();
const { data, loading, refresh } = useAnalytics(client);
React.useEffect(() => {
// Connect to WebSocket for real-time updates
const ws = new WebSocket('wss://your-websocket-server.com');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'payment_received') {
// Refresh analytics when new payment is received
refresh();
}
};
return () => ws.close();
}, [refresh]);
if (loading) return <div>Loading real-time data...</div>;
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2>Real-time Revenue Dashboard</h2>
<div style={{
backgroundColor: '#10B981',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px'
}}>
🟢 LIVE
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '20px' }}>
<div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
<h3>Total Revenue</h3>
<p style={{ fontSize: '28px', fontWeight: 'bold', color: '#10B981' }}>
${data?.overview?.total_revenue_30d?.toLocaleString() || '0'}
</p>
</div>
<div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
<h3>Active Subscriptions</h3>
<p style={{ fontSize: '28px', fontWeight: 'bold', color: '#3B82F6' }}>
{data?.overview?.active_subscriptions?.toLocaleString() || '0'}
</p>
</div>
<div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
<h3>Success Rate</h3>
<p style={{ fontSize: '28px', fontWeight: 'bold', color: '#F59E0B' }}>
{data?.overview?.payment_success_rate || '0'}%
</p>
</div>
</div>
</div>
);
}
import React from 'react';
import { SubLoopError, useAnalytics } from 'subloop-react';
function AnalyticsWithErrorHandling() {
const { data, loading, error } = useAnalytics(client);
if (loading) return <div>Loading...</div>;
if (error) {
if (error instanceof SubLoopError) {
if (error.isAuthenticationError()) {
return <div>Authentication failed. Please check your API key.</div>;
} else if (error.isRateLimitError()) {
return <div>Rate limit exceeded. Please try again later.</div>;
} else if (error.isValidationError()) {
return <div>Validation error: {JSON.stringify(error.getDetails())}</div>;
}
}
return <div>Error: {error.message}</div>;
}
return <div>Analytics data loaded successfully!</div>;
}
The SDK includes full TypeScript support:
import React from 'react';
import { SubLoopProvider, useAnalytics, AnalyticsData } from 'subloop-react';
interface DashboardProps {
apiKey: string;
}
const Dashboard: React.FC<DashboardProps> = ({ apiKey }) => {
return (
<SubLoopProvider apiKey={apiKey}>
<AnalyticsComponent />
</SubLoopProvider>
);
};
const AnalyticsComponent: React.FC = () => {
const client = useSubLoopContext();
const { data, loading, error }: {
data: AnalyticsData | null;
loading: boolean;
error: Error | null;
} = useAnalytics(client);
// Component implementation...
};
MIT License. See LICENSE for more information.