UNPKG

@subscribe.dev/react

Version:

React hooks and components for SubscribeDev - provides context and hooks for managing AI predictions with billing and rate limiting

538 lines (431 loc) 14.3 kB
# @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 ```bash npm install @subscribe.dev/react # or yarn add @subscribe.dev/react # or bun add @subscribe.dev/react ``` ### Peer Dependencies ```bash 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 ```tsx 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: ```tsx function MultiModalApp() { const { client, isSignedIn } = useSubscribeDev(); const [image, setImage] = useState(null); const [description, setDescription] = useState(''); const handleGenerate = async (prompt) => { if (!isSignedIn || !client) return; try { // Generate image and description in parallel 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: ```tsx 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: ```tsx 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" }; // Add to gallery and keep latest 100 images 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: ```tsx function PremiumFeatures() { const { subscribe, client } = useSubscribeDev(); const { isSubscribed, subscriptionStatus } = useSubscribeDevAuth(); const runPremiumModel = async () => { if (!isSubscribed) { subscribe(); // Opens subscription modal return; } const result = await client.run("premium-model/advanced", { input: { /* premium model parameters */ } }); 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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx function AdvancedApp() { const { client } = useSubscribeDev(); // Monitor rate limits const checkRateLimits = async () => { const limits = await client.getRateLimits(); console.log('Concurrent requests:', limits.concurrent.currentRequests); console.log('Hourly limit reached:', !limits.hour?.allowed); }; // Manage session data const syncSessionData = async () => { const sessionData = await client.getSessionData(); await client.setStorage({ ...sessionData, lastActivity: Date.now() }); }; // Get subscription details 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. ```tsx <SubscribeDevProvider projectToken="pub_your_project_token" // Required: Your project API key clerkPublishableKey="pk_your_clerk_key" // Required: Clerk authentication baseUrl="https://api.subscribe.dev" // Optional: Custom API URL > <YourApp /> </SubscribeDevProvider> ``` ### useSubscribeDev() Main hook providing access to client, authentication, and storage. ```tsx const { client, // SubscribeDevClient instance token, // Current auth token isSignedIn, // Authentication status subscribe, // Open subscription modal useStorage, // Storage hook factory useUsage // Usage hook factory } = useSubscribeDev(); ``` ### useSubscribeDevAuth() Authentication and subscription management. ```tsx const { isSignedIn, // Authentication status isSubscribed, // Subscription status subscribe, // Open subscription modal subscriptionStatus, // Detailed subscription info subscriptionLoading // Loading state } = useSubscribeDevAuth(); ``` ### useStorage(key, initialValue) Persistent storage with cloud synchronization. ```tsx const [value, setValue, syncStatus] = useStorage('storage-key', defaultValue); // syncStatus: 'local' | 'syncing' | 'synced' | 'error' ``` ### useUsage() Get user balance information including allocated, used, and remaining credits. ```tsx const { useUsage } = useSubscribeDev(); const { allocatedCredits, usedCredits, remainingCredits, loading, error, refreshUsage } = useUsage(); // allocatedCredits: number - Total credits allocated to user // usedCredits: number - Credits consumed by user // remainingCredits: number - Credits remaining for user // loading: boolean - Loading state when fetching balance data // error: string | null - Error message if fetch fails // refreshUsage: () => Promise<void> - Manually refresh usage data ``` ### 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: ```tsx import { SignIn, SignInButton, UserButton } from '@subscribe.dev/react'; // Full sign-in form <SignIn /> // Sign-in trigger button <SignInButton>Sign In</SignInButton> // User profile menu <UserButton /> ``` ## Type Definitions ```tsx 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 1. **Error Handling**: Always wrap AI calls in try-catch blocks 2. **Loading States**: Provide loading indicators for better UX 3. **Cost Awareness**: Monitor credits and inform users of costs 4. **Storage Sync**: Check sync status for critical data operations 5. **Rate Limiting**: Handle rate limit errors gracefully 6. **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