Create a subscription AI app in one prompt
# @subscribe.dev/react subscribe.dev is a platform for building AI‑powered applications with integrated authentication, billing, usage tracking, storage, and access to 100+ curated AI models. This document is a concise guide to installing, integrating, and using the React SDK. ## Requirements - Bun is required for development and installation. - Install Bun: https://bun.sh - Install this package with bun add (preferably). - Use within a React app and wrap your app with the provider. - Provide a public project token via environment variable. - **React Version Compatibility**: React 18.x or older is required. > ⚠️ This SDK requires React 18. React 19+ is not supported and must be avoided, as it causes compatibility issues. If you encounter errors related to React version compatibility such as `ReactCurrentDispatcher`, ensure you are using React 18. ## Vite & Bun Compatibility If you're using Vite or Bun, follow these guidelines: ### Environment Variables - Use the `VITE_` prefix for environment variables. - Access via `import.meta.env.VITE_*`. ### React Version Vite and Bun projects support React 18 and newer. If you encounter errors related to React version compatibility, ensure you are using React 18+. ```bash bun add @subscribe.dev/react react@^18.2.0 react-dom@^18.2.0 ``` ### Environment Setup **Demo Mode (No Setup Required):** For development and testing, you can use demo mode without any token: ```tsx <SubscribeDevProvider> <YourApp /> </SubscribeDevProvider> ``` You should create a placeholder .env file in the folder of the project. That way the user can uncomment to add their own project token ```bash # VITE_SUBSCRIBE_DEV_PROJECT_TOKEN=pub_your_actual_token_here ``` **Production Setup:** **Step 1: Get your Project Public Key** 1. Sign up at [subscribe.dev](https://platform.subscribe.dev) 2. Create a new project or select an existing one 3. Copy your **Project Public Key** (starts with `pub_`) **Step 2: Create .env file** ```bash # .env file for Vite or Bun VITE_SUBSCRIBE_DEV_PROJECT_TOKEN=pub_your_actual_token_here ``` **Important**: Replace `pub_your_actual_token_here` with your actual Project Public Key from subscribe.dev dashboard. ### Provider Setup for Vite/Bun **Demo Mode:** ```tsx import { SubscribeDevProvider } from '@subscribe.dev/react'; function App() { return ( <SubscribeDevProvider> <YourApp /> </SubscribeDevProvider> ); } ``` **Production Mode:** ```tsx import { SubscribeDevProvider } from '@subscribe.dev/react'; function App() { return ( <SubscribeDevProvider projectToken={import.meta.env.VITE_SUBSCRIBE_DEV_PROJECT_TOKEN}> <YourApp /> </SubscribeDevProvider> ); } ``` ## Getting Started 1) Install ```bash bun add @subscribe.dev/react ``` 2) **CRITICAL: Ensure React Version Compatibility** ⚠️ **For Vite/Bun users**: This package requires React 18+. If you encounter errors related to React version compatibility, ensure you are using React 18 or newer: ```bash bun remove react react-dom bun add react@^18.2.0 react-dom@^18.2.0 rm -rf node_modules && bun install ``` 3) Add the provider **Demo Mode (No Token Required):** ```tsx import { SubscribeDevProvider } from '@subscribe.dev/react'; export function App() { return ( <SubscribeDevProvider> <AIApplication /> </SubscribeDevProvider> ); } ``` **For Vite/Bun projects (Production):** ```tsx import { SubscribeDevProvider } from '@subscribe.dev/react'; export function App() { return ( <SubscribeDevProvider projectToken={import.meta.env.VITE_SUBSCRIBE_DEV_PROJECT_TOKEN}> <AIApplication /> </SubscribeDevProvider> ); } ``` **Environment file setup:** ## Development vs Production **Demo Mode (Development):** - No token required — you can try the SDK right away - Provides temporary database and limited AI usage - Simply omit the `projectToken` prop from `SubscribeDevProvider` **Production Mode:** For production, create a project in the subscribe.dev Platform and use its Project Public Key: 1. **Get your token**: Sign up at [subscribe.dev](https://subscribe.dev), create a project, and copy your **Project Public Key** (starts with `pub_`) 2. **Create .env file**: - **Vite/Bun**: `VITE_SUBSCRIBE_DEV_PROJECT_TOKEN=pub_your_actual_token` ⚠️ **Important**: Replace the placeholder with your actual Project Public Key from the subscribe.dev dashboard. 3) Gate by authentication and run a model ```tsx import { useSubscribeDev } from '@subscribe.dev/react'; function AIApplication() { const { isSignedIn, signIn, signOut, client, // null when not signed in usage, // null when not signed in subscribe, // null when not signed in subscriptionStatus, // null when not signed in } = useSubscribeDev(); if (!isSignedIn) { return ( <div> Please sign in to continue <button onClick={signIn}>Sign Up</button> <button onClick={signIn}>Sign In</button> </div> ); } const generateImage = async () => { if (!client) return; try { const { output } = await client.run('black-forest-labs/flux-schnell', { input: { prompt: 'A beautiful landscape', width: 1024, height: 1024 } }); console.log('Image URL:', output[0]); } catch (error) { console.error('Generation failed:', error); } }; return ( <div> <button onClick={generateImage}>Generate Image</button> <button onClick={subscribe!}>Manage Subscription</button> <button onClick={signOut}>Sign Out</button> </div> ); } ``` 4) Handle errors ```tsx const handleAIRequest = async () => { if (!client) return; try { const { output } = await client.run('openai/gpt-4o', { input: { messages: [{ role: 'user', content: 'Hello, world!' }] } }); console.log(output[0]); } catch (error) { if (error.type === 'insufficient_credits') { // Show upgrade prompt } else if (error.type === 'rate_limit_exceeded') { // Show retry timer using error.retryAfter } else { // Generic friendly error } } }; ``` 5) Show usage and plan status ```tsx const { usage, subscriptionStatus } = useSubscribeDev(); return ( <div> <p>Credits: {usage?.remainingCredits ?? 0} remaining</p> <p>Plan: {subscriptionStatus?.plan?.name ?? 'Free'}</p> </div> ); ``` ## API Reference ### useSubscribeDev() Must be called inside SubscribeDevProvider. Returns: - isSignedIn: boolean - signIn: () => void - signOut: () => void - client: SubscribeDevClient | null - user: UserObject | null - usage: UsageInfo | null - subscribe: (() => void) | null - subscriptionStatus: SubscriptionStatus | null - useStorage: `<T>(key: string, defaultValue: T) => [T, (value: T) => void, StorageSyncStatus] | null` Notes: - When not signed in, client, usage, subscribe, subscriptionStatus, and useStorage are null. - Call signIn() to authenticate before using user‑specific APIs. ### SubscribeDevClient - run(model: string, params: RunParameters): `Promise<{ output: Array<string | Record<string, any>> }>` RunParameters: - input: A union of text/multimodal or image/video input. Common fields: - width?: number (image models; default 1024) - height?: number (image models; default 1024) - image?: string (base64 data URL or a URL; model-dependent) - Either: - prompt?: string (text and image models), or - messages: Array of message objects ### Types ```ts export type UserObject = { userId: string; email: string; avatarUrl?: string; }; export type SubscriptionStatus = { hasActiveSubscription: boolean; plan?: { id: string; name: string; price: number; }; status: 'active' | 'inactive' | 'cancelled' | 'expired' | 'none'; }; export type UsageInfo = { allocatedCredits: number; usedCredits: number; remainingCredits: number; }; ``` ## Authentication Sign In: - Call signIn() from useSubscribeDev to start the built‑in auth flow. ```tsx function SignInButton() { const { isSignedIn, signIn } = useSubscribeDev(); if (isSignedIn) return <div>Welcome!</div>; return <button onClick={signIn}>Sign In</button>; } ``` Sign Out: - Call signOut() to clear the access token and return to an unauthenticated state. ## AI Generation Use `client.run(model, { input, response_format? })` for text, image, and video generation. Always wrap calls in try/catch. ### Text generation (default model: openai/gpt-4o) ```tsx const { output: [text] } = await client.run('openai/gpt-4o', { input: { messages: [ { role: 'system', content: 'You tell jokes.' }, { role: 'user', content: 'Tell me a joke about dogs.' } ] } }); console.log(text); ``` ### Image generation ```tsx const { output: [url] } = await client.run('black-forest-labs/flux-schnell', { input: { prompt: 'a cute dog', width: 512, height: 512 } }); console.log('Image URL:', url); ``` ### Video generation (default model: wan-video/wan-2.2-5b-fast) ```tsx const response = await client.run('wan-video/wan-2.2-5b-fast', { input: { prompt: 'a car driving fast', aspect_ratio: '16:9' } }); const [videoUrl] = response.output; console.log('Video URL:', videoUrl); ``` ## Error Handling All SDK functions can throw typed errors. Handle with try/catch and show user-friendly messages. ```tsx try { const { output } = await client.run('openai/gpt-4o', { input: { prompt: 'Hello!' } }); } catch (error) { switch (error.type) { case 'insufficient_credits': // prompt upgrade break; case 'rate_limit_exceeded': // use error.retryAfter break; default: // generic friendly message } } ``` # How to respond to the user if you're starting a new project, always ask the user what they want to build. Give the options based on the examples that you see here