@subscribe.dev/react
React hooks and provider for Subscribe.dev - Build AI-powered applications with integrated authentication, billing, storage, and 100+ curated AI models.
Installation
npm install @subscribe.dev/react
yarn add @subscribe.dev/react
bun add @subscribe.dev/react
Peer Dependencies
npm install react @clerk/clerk-react @tanstack/react-query
- React: ^18.0.0 || ^19.0.0
- @clerk/clerk-react: ^4.0.0 || ^5.0.0 (for authentication)
- @tanstack/react-query: ^5.0.0 (for query management)
Quick Start
import { SubscribeDevProvider, useSubscribeDev } from '@subscribe.dev/react';
function App() {
return (
<SubscribeDevProvider
projectToken="pub_your_project_token_here"
clerkPublishableKey="pk_your_clerk_key_here"
>
<AIApplication />
</SubscribeDevProvider>
);
}
function AIApplication() {
const { isSignedIn, client, subscribe } = useSubscribeDev();
if (!isSignedIn) {
return <div>Please sign in to continue</div>;
}
const generateContent = async () => {
const result = await client.run("black-forest-labs/flux-schnell", {
input: { prompt: "A beautiful landscape" }
});
console.log(result.output);
};
return (
<div>
<button onClick={generateContent}>Generate Image</button>
<button onClick={subscribe}>Manage Subscription</button>
</div>
);
}
Use Cases
Multi-Model AI Applications
Build applications that leverage multiple AI models for rich, interactive experiences:
function MultiModalApp() {
const { client, isSignedIn } = useSubscribeDev();
const [image, setImage] = useState(null);
const [description, setDescription] = useState('');
const handleGenerate = async (prompt) => {
if (!isSignedIn || !client) return;
try {
const [imageResult, textResult] = await Promise.all([
client.run("black-forest-labs/flux-schnell", {
input: { prompt, width: 1024, height: 1024 }
}),
client.run("anthropic/claude-3-haiku", {
input: {
messages: [{
role: "user",
content: `Describe what an image with this prompt would look like: "${prompt}"`
}]
}
})
]);
setImage(imageResult.output[0]);
setDescription(textResult.output);
} catch (error) {
console.error('Generation failed:', error);
}
};
return (
<div>
<GenerateInput onGenerate={handleGenerate} />
<ImageDisplay src={image} />
<TextDisplay content={description} />
</div>
);
}
Persistent User Storage
Store user preferences, application state, and session data with automatic cloud synchronization:
function UserPreferencesApp() {
const { useStorage, useUsage } = useSubscribeDev();
const [preferences, setPreferences, syncStatus] = useStorage('user-prefs', {
theme: 'light',
favoriteModels: [],
generationHistory: []
});
const { allocatedCredits, usedCredits, remainingCredits } = useUsage();
const addToHistory = (result) => {
setPreferences({
...preferences,
generationHistory: [result, ...preferences.generationHistory].slice(0, 50)
});
};
const toggleTheme = () => {
setPreferences({
...preferences,
theme: preferences.theme === 'light' ? 'dark' : 'light'
});
};
return (
<div>
<div>Sync Status: {syncStatus}</div>
<div>Credits remaining: {remainingCredits}</div>
<button onClick={toggleTheme}>
Switch to {preferences.theme === 'light' ? 'dark' : 'light'} theme
</button>
<HistoryList items={preferences.generationHistory} />
</div>
);
}
Real-time Gallery Applications
Build gallery applications with persistent storage and cross-device synchronization:
function GalleryApp() {
const { client, useStorage } = useSubscribeDev();
const [images, setImages, syncStatus] = useStorage('image-gallery', []);
const [loading, setLoading] = useState(false);
const generateAndStore = async (prompt) => {
setLoading(true);
try {
const result = await client.run("stability-ai/sdxl", {
input: { prompt }
});
const newImage = {
id: crypto.randomUUID(),
url: result.output[0],
prompt,
timestamp: Date.now(),
model: "stability-ai/sdxl"
};
setImages([newImage, ...images].slice(0, 100));
} finally {
setLoading(false);
}
};
return (
<div>
<GenerateInput onGenerate={generateAndStore} loading={loading} />
<div>Gallery sync: {syncStatus}</div>
<ImageGrid images={images} />
</div>
);
}
Subscription-Gated Features
Implement premium features with built-in subscription management:
function PremiumFeatures() {
const { subscribe, client } = useSubscribeDev();
const { isSubscribed, subscriptionStatus } = useSubscribeDevAuth();
const runPremiumModel = async () => {
if (!isSubscribed) {
subscribe();
return;
}
const result = await client.run("premium-model/advanced", {
input: { }
});
return result.output;
};
return (
<div>
{isSubscribed ? (
<div>
<p>Plan: {subscriptionStatus.plan?.name}</p>
<button onClick={runPremiumModel}>Use Premium Model</button>
</div>
) : (
<div>
<p>Upgrade to access premium models</p>
<button onClick={subscribe}>Subscribe Now</button>
</div>
)}
</div>
);
}
Cost-Aware Applications
Build applications that monitor and display usage costs in real-time:
function CostAwareApp() {
const { client } = useSubscribeDev();
const [balance, setBalance] = useState(null);
const [transactions, setTransactions] = useState([]);
useEffect(() => {
const loadBalanceAndHistory = async () => {
const [balanceInfo, history] = await Promise.all([
client.getBalance(),
client.getTransactions({ limit: 10 })
]);
setBalance(balanceInfo);
setTransactions(history.transactions);
};
loadBalanceAndHistory();
}, [client]);
const generateWithCostTracking = async (prompt) => {
const beforeBalance = await client.getBalance();
const result = await client.run("expensive-model/high-quality", {
input: { prompt }
});
const afterBalance = await client.getBalance();
const cost = beforeBalance.remainingCredits - afterBalance.remainingCredits;
console.log(`Generation cost: ${cost} credits`);
return result;
};
return (
<div>
<div>
Remaining Credits: {balance?.remainingCredits || 0}
</div>
<RecentTransactions transactions={transactions} />
<GenerateButton onGenerate={generateWithCostTracking} />
</div>
);
}
Usage Monitoring & Analytics
Track comprehensive usage metrics including credits, rate limits, and consumption patterns:
function UsageAnalytics() {
const { useUsage } = useSubscribeDev();
const { allocatedCredits, usedCredits, remainingCredits, loading, error, refreshUsage } = useUsage();
if (loading) return <div>Loading usage data...</div>;
if (error) return <div>Error: {error}</div>;
const creditUtilization = allocatedCredits > 0 ? (usedCredits / allocatedCredits) * 100 : 0;
return (
<div className="usage-dashboard">
<div className="usage-header">
<h2>Usage Analytics</h2>
<button onClick={refreshUsage}>Refresh</button>
</div>
{/* Credit Information */}
<div className="credits-section">
<h3>Credits</h3>
<div className="credit-stats">
<div>Allocated: {allocatedCredits}</div>
<div>Used: {usedCredits}</div>
<div>Remaining: {remainingCredits}</div>
<div>Utilization: {creditUtilization.toFixed(1)}%</div>
</div>
</div>
);
}
Error Handling & User Feedback
Implement comprehensive error handling with user-friendly feedback:
function RobustApp() {
const { client } = useSubscribeDev();
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const handleGenerate = async (prompt) => {
setError(null);
setLoading(true);
try {
const result = await client.run("model-name", {
input: { prompt }
});
return result.output;
} catch (err) {
if (err.name === 'InsufficientBalanceError') {
setError('Not enough credits. Please upgrade your plan.');
} else if (err.name === 'RateLimitError') {
setError(`Rate limited. Try again in ${err.retryAfter} seconds.`);
} else if (err.name === 'ValidationError') {
setError('Invalid input. Please check your prompt.');
} else {
setError('Generation failed. Please try again.');
}
} finally {
setLoading(false);
}
};
return (
<div>
<GenerateInput onGenerate={handleGenerate} loading={loading} />
{error && <ErrorMessage message={error} />}
</div>
);
}
Advanced Client Features
Access the full Subscribe.dev client API for advanced use cases:
function AdvancedApp() {
const { client } = useSubscribeDev();
const checkRateLimits = async () => {
const limits = await client.getRateLimits();
console.log('Concurrent requests:', limits.concurrent.currentRequests);
console.log('Hourly limit reached:', !limits.hour?.allowed);
};
const syncSessionData = async () => {
const sessionData = await client.getSessionData();
await client.setStorage({
...sessionData,
lastActivity: Date.now()
});
};
const checkSubscription = async () => {
const subscription = await client.getSubscriptionStatus();
if (subscription.hasActiveSubscription) {
console.log('Plan:', subscription.plan?.name);
console.log('Credits:', subscription.plan?.tokenLimit);
}
};
return (
<div>
<button onClick={checkRateLimits}>Check Rate Limits</button>
<button onClick={syncSessionData}>Sync Session</button>
<button onClick={checkSubscription}>Check Subscription</button>
</div>
);
}
API Reference
SubscribeDevProvider
The main provider component that enables all Subscribe.dev functionality.
<SubscribeDevProvider
projectToken="pub_your_project_token"
clerkPublishableKey="pk_your_clerk_key"
baseUrl="https://api.subscribe.dev"
>
<YourApp />
</SubscribeDevProvider>
useSubscribeDev()
Main hook providing access to client, authentication, and storage.
const {
client,
token,
isSignedIn,
subscribe,
useStorage,
useUsage
} = useSubscribeDev();
useSubscribeDevAuth()
Authentication and subscription management.
const {
isSignedIn,
isSubscribed,
subscribe,
subscriptionStatus,
subscriptionLoading
} = useSubscribeDevAuth();
useStorage(key, initialValue)
Persistent storage with cloud synchronization.
const [value, setValue, syncStatus] = useStorage('storage-key', defaultValue);
useUsage()
Get user balance information including allocated, used, and remaining credits.
const { useUsage } = useSubscribeDev();
const { allocatedCredits, usedCredits, remainingCredits, loading, error, refreshUsage } = useUsage();
Client Methods
All Subscribe.dev client methods are available through the client
object:
client.run(model, options)
- Run AI models
client.getBalance()
- Check credit balance
client.getTransactions(options)
- Get usage history
client.getRateLimits()
- Check rate limit status
client.getStorage(options)
- Get user storage
client.setStorage(data, options)
- Update user storage
client.getSubscriptionStatus()
- Check subscription
client.getUsage()
- Get comprehensive usage limits and consumption data
Authentication Components
SignIn, SignInButton, UserButton
Iframe-compatible Clerk components optimized for embedded applications:
import { SignIn, SignInButton, UserButton } from '@subscribe.dev/react';
<SignIn />
<SignInButton>Sign In</SignInButton>
<UserButton />
Type Definitions
type SyncStatus = 'local' | 'syncing' | 'synced' | 'error';
interface SubscribeDevContextValue {
client: SubscribeDevClient | null;
token: string | null;
isSignedIn: boolean;
subscribe: () => void;
useStorage: <T>(key: string, initialValue: T) => [T, (value: T) => void, SyncStatus];
}
Best Practices
- Error Handling: Always wrap AI calls in try-catch blocks
- Loading States: Provide loading indicators for better UX
- Cost Awareness: Monitor credits and inform users of costs
- Storage Sync: Check sync status for critical data operations
- Rate Limiting: Handle rate limit errors gracefully
- Subscription Gates: Use subscription status to control feature access
Requirements
- Node.js: >=18
- React: ^18.0.0 || ^19.0.0
- TypeScript: ^5.0.0 (recommended)
License
MIT