
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
@subscribe.dev/react
Advanced tools
React hooks and components for SubscribeDev - provides context and hooks for managing AI predictions with billing and rate limiting
React hooks and provider for Subscribe.dev - Build AI-powered applications with integrated authentication, billing, storage, and 100+ curated AI models.
Subscribe.dev provides a secure, production-ready platform that leverages industry-standard services: Stripe handles all payment processing (ensuring zero exposure of payment data), our integrated authentication system manages user sign-in flows, and our platform manages AI model access and usage tracking.
npm install @subscribe.dev/react # not recommended
# or
yarn add @subscribe.dev/react # not recommended
# or
bun add @subscribe.dev/react # recommended!
Note: Subscribe.dev handles authentication through its own secure infrastructure - no additional authentication setup is required.
The SubscribeDevProvider
is a React context provider that wraps your application. It provides the necessary context for the useSubscribeDev
hook to function correctly.
To use it, simply wrap it at the root level around your React application:
import { SubscribeDevProvider } from '@subscribe.dev/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
export default function Root() {
return (
<SubscribeDevProvider
projectToken={process.env.REACT_APP_SUBSCRIBE_DEV_PROJECT_TOKEN} // optional for production
// authorizationUrl="https://custom-auth.example.com" // optional custom auth URL
// baseUrl="https://api.subscribe.dev" // optional custom API URL
>
<App />
</SubscribeDevProvider>
);
}
and call the useSubscribeDev
hook somewhere:
import {SubscribeDevProvider, useSubscribeDev} from '@subscribe.dev/react';
import {useState} from 'react';
export default function App() {
const {
isSignedIn, // indicates auth status
signIn, // a function to authenticate
client, // an instance of the SubscribeDevClient
user, // the signed in user object or null
usage, // an object showing the consumed credits and balance for the user
subscribe, // a function to trigger the subscription flow
subscriptionStatus, // an object indicating subscription status
useStorage, // a React hook to store key-value data for the user
} = useSubscribeDev();
const [email, setEmail] = useState('');
// ... your component logic
}
When you call useSubscribeDev()
, you get the following values:
isSignedIn: boolean
- indicates whether the user is authenticatedsignIn: () => void
- function to trigger the authentication flowsignOut: () => void
- function to sign out the current user (clears access token)client: SubscribeDevClient
- instance with a run()
method for executing AI modelsuser: UserObject | null
- the current user object, or null
if not authenticatedusage: UsageInfo
- credits used and remaining for the user (updates automatically)subscribe: () => void
- triggers the subscription flow in an iframesubscriptionStatus: SubscriptionStatus
- indicates subscription tier and statususeStorage: <T>(key: string) => [T, (value: T) => void]
- hook for persisting user data across sessionsThe hook provides the following types:
export type RunParameters = {
input: {
width?: number; // for image models only -- defaults to 1024
height?: number // for image models only -- defaults to 1024,
image?: string // base64 encoded or a URL to an image
} & ({
prompt?: string // for image models and text completion models
} | {
messages: Array<{ role: string, content: string } | {
type: 'text' | 'image_url' // for multimodal message contnt
text?: string
image_url?: {
url: string
detail?: 'low' | 'high' | 'auto'
}
}> // for text completion models only
}),
response_format: { // for text completion models only
type: 'json_object' // to request any JSON back
} | {
type: 'json_schema' // to request an OpenAPI JSON Schema-compliant object
json_schema: {
name: string
strict?: boolean
schema: Record<string, unknown>
}
} | ZodObject // Also accept native Zod schemas directly as the `response_format`
}
type RunOutput = {
output: Array<string | Record<string, any>> // for text completion models, only one element representing the completed text or JSON. For image models, generally a URL, sometimes many URLs if there are multiple images generated.
}
type UserObject = {
userId: string
email: string
avatarUrl?: string
}
type SubscriptionStatus = {
hasActiveSubscription: boolean
plan?: {
id: string
name: string
price: number
}
status: 'active' | 'inactive' | 'cancelled' | 'expired' | 'none'
}
type UsageInfo = {
allocatedCredits: number
usedCredits: number
remainingCredits: number
}
The client.run()
method signature:
run: async (model: string, input: RunParameters) => Promise<{ output: RunOutput }>
isSignedIn
before using user-specific SubscribeDev functions - if they aren't, call signIn()
useSubscribeDev
hook outside of the SubscribeDevProvider
context, as it will throw an errorprojectToken
, but this will result in using demo mode, which is intended for developmentprojectToken
from your Subscribe.dev dashboardGenerally the client is mostly just used for its function run
which executes AI requests against your project and users' allocated credits.
The client has lower-level functions, but the React hooks mostly fill the role of calling these, and use the client under the hood. There is generally no need to use the client, but it exposed from the provider in cases of specialized use. The documentation for the client is available here.
Subscribe.dev provides a streamlined authentication system with three distinct states:
Demo Mode - No project token, no user access token
signIn()
redirects to the demo flowProject Mode - Project token provided, no user access token
Authenticated Mode - User has access token
To authenticate users, simply call the signIn()
function from the useSubscribeDev
hook:
import { useSubscribeDev } from '@subscribe.dev/react';
function SignInButton() {
const { isSignedIn, signIn } = useSubscribeDev();
if (isSignedIn) {
return <div>Welcome! You're signed in.</div>;
}
return (
<button onClick={signIn}>
Sign In
</button>
);
}
To sign out users, call the signOut()
function. This clears the user's access token and returns them to an unauthenticated state:
import { useSubscribeDev } from '@subscribe.dev/react';
function SignOutButton() {
const { isSignedIn, signOut } = useSubscribeDev();
if (!isSignedIn) {
return null; // Don't show sign out if not signed in
}
return (
<button onClick={signOut}>
Sign Out
</button>
);
}
After calling signOut()
, the user will be returned to either project mode (state 2) if a projectToken
is provided, or demo mode (state 1) if no project token is available.
signIn()
is calledaccessToken
in the URLprojectToken
- users will get demo projects automaticallyprojectToken
from the Subscribe.dev dashboard for your specific project// Demo mode (development)
<SubscribeDevProvider>
<App />
</SubscribeDevProvider>
// Production mode
<SubscribeDevProvider projectToken="sk_proj_your_token_here">
<App />
</SubscribeDevProvider>
import { useSubscribeDev } from '@subscribe.dev/react';
import { useState } from 'react';
function MyAIApp() {
const {
client,
isSignedIn,
signIn,
signOut,
usage,
subscribe,
subscriptionStatus
} = useSubscribeDev();
const [result, setResult] = useState('');
const runAIModel = async () => {
if (!client) {
// State 1: No client available (demo mode, need to sign in)
alert('Please sign in to use AI models');
return;
}
try {
const response = await client.run('openai/gpt-4o', {
input: { prompt: 'Tell me a joke about AI' }
});
setResult(response.output[0]);
} catch (error) {
console.error('AI request failed:', error);
}
};
// State 1: No tokens (demo mode)
if (!client) {
return (
<div>
<h1>Welcome to AI Demo</h1>
<p>Sign in to get started with your demo project!</p>
<button onClick={signIn}>Get Started</button>
</div>
);
}
// State 2: Project token only (signed out)
if (!isSignedIn) {
return (
<div>
<h1>AI App</h1>
<p>You can use basic features, but sign in for full functionality!</p>
<button onClick={runAIModel}>Try AI (Limited)</button>
<button onClick={signIn}>Sign In for Full Access</button>
</div>
);
}
// State 3: Fully authenticated
return (
<div>
<h1>AI App - Welcome!</h1>
<div>Credits: {usage.remainingCredits}/{usage.allocatedCredits}</div>
<button onClick={runAIModel}>Generate AI Content</button>
<button onClick={signOut}>Sign Out</button>
{!subscriptionStatus?.hasActiveSubscription && (
<button onClick={subscribe}>Upgrade for More Credits</button>
)}
{result && (
<div>
<h3>AI Result:</h3>
<p>{result}</p>
</div>
)}
</div>
);
}
Q: Does Subscribe.dev ever see my users' credit card information?
A: No. All payment processing is handled directly by Stripe. Subscribe.dev never receives or stores payment data.
Q: Do you manage user passwords or authentication data?
A: Subscribe.dev uses secure, industry-standard authentication practices. We handle user sign-in through our secure authentication infrastructure and only store necessary user identification tokens.
Q: What happens if Stripe or Subscribe.dev authentication services are down?
A: Payment and authentication flows would be temporarily unavailable, but your AI model usage would continue to work for already-authenticated users with existing credits.
All Subscribe.dev functions can throw errors, which you can catch using standard JavaScript error handling:
import { useSubscribeDev } from '@subscribe.dev/react';
function MyComponent() {
const { client } = useSubscribeDev();
const handleAIRequest = async () => {
try {
const result = await client.run('openai/gpt-4o', {
input: { prompt: "Hello, world!" }
});
console.log(result.output[0]);
} catch (error) {
// Errors include type, message, and relevant details
if (error.type === 'insufficient_credits') {
console.error('Not enough credits:', error.message);
// Handle insufficient credits (e.g., prompt user to subscribe)
} else if (error.type === 'rate_limit_exceeded') {
console.error('Rate limited:', error.retryAfter);
// Handle rate limiting (e.g., show retry timer)
} else {
console.error('AI request failed:', error.message);
}
}
};
return <button onClick={handleAIRequest}>Run AI Model</button>;
}
For detailed error types and handling strategies, refer to the error documentation.
Subscribe.dev is designed to work seamlessly with your existing development workflow:
For production applications, comprehensive observability is available through the Subscribe.dev platform dashboard:
The React client is designed for embedding in user-facing applications and only exposes public information and developer-friendly errors. For administrative access, team management, and detailed platform insights, use the web dashboard.
The usage object from the provider will update when you run requests through the SubscribeDevClient
. If we listen to the value from the provider hook, it should update automatically, but it may need to be present in e.g. the dependency array of a useEffect.
Calling subscribe()
is a magic function that will guide the user through a subscription flow powered by Stripe and allow them to manage their current subscription. All payment processing is handled securely by Stripe - Subscribe.dev has zero exposure to payment data, ensuring maximum security and compliance.
The subscription status will automatically update when changed, and we can trust the value from the provider.
The user object contains authenticated user information extracted from the access token. The object has the following structure:
type UserObject = {
userId: string // Unique user identifier (subject from token)
email: string // User's email address
avatarUrl?: string // Optional user avatar image URL
}
The user object can be used to display user information, profile badges, or personalize the application experience. On signing in, this will update to a populated value, but will be null
before authentication.
Persistent storage with cloud synchronization. Comes from useSubscribeDev
hook.
The useStorage
hook should be called when we want to persist data at a component-level or application-level for users beyond their current session. This data is saved on the backend of subscribe.dev
and will persist across devices if the same authentication method is used.
const { useStorage } = useSubscribeDev(); // the hook comes from inside the subscribe dev provider
const [value, setValue, syncStatus] = useStorage('storage-key', defaultValue);
// syncStatus: 'local' | 'syncing' | 'synced' | 'error'
type AppState = {
lastState: string,
counter: number
};
export function ServerPersistedCounter(props) {
const [storedObject, setStoredObject] = useStorage<AppState>('app-state');
const updateCounter = () => {
setStoredObject({
...storedObject,
counter: (storedObject?.counter || 0) + 1
})
}
return (
<div>
<p>Counter: {storedObject?.counter || 0}</p>
<button onClick={updateCounter}>Click Me To Count</button>
</div>
);
}
// Standard usage, text completion:
const result = await client.run('openai/gpt-4o', {
input: {
prompt: "Tell me a joke about AI"
}
});
console.log("Output Joke: ", result.output[0]);
// Standard usage, JSON completion:
const {output} = await client.run('openai/gpt-4o', {
input: {
prompt: "Tell me a joke about AI that has the following shape: { setup: string, punchline: string }"
},
response_format: {
type: 'json_object'
}
});
console.log("Output Joke JSON: ", output[0]);
// JSON Schema completion:
const {output: [jokeResponseJson]} = await client.run('openai/gpt-4o', {
input: {
prompt: "Tell me a joke about AI"
},
response_format: {
type: 'json_schema',
json_schema: {
name: 'Joke',
strict: true,
schema: {
type: 'object',
properties: {
setup: {type: 'string'},
punchline: {type: 'string'}
},
required: ['setup', 'punchline']
}
}
}
});
// Zod Usage
const jokeSchema = z.object({
setup: z.string(),
punchline: z.string()
});
const {output: [jokeResponseZodJson]} = await client.run('openai/gpt-4o', {
input: {
prompt: "Tell me a joke about AI"
},
response_format: jokeSchema // directly passing a Zod schema
});
console.log("Output Joke JSON Schema: ", jokeResponseZodJson);
// multimodal usage:
const {output: [multimodalResponse]} = await client.run('openai/gpt-4o', {
input: {
messages: [
{role: "system", content: "You identify stuff."},
{role: 'user', content: 'What is this image?'},
{type: 'image_url', image_url: {url: 'https://example.com/image.jpg'}}
]
}
});
console.log("Multimodal Response: ", multimodalResponse);
// generate an image of a dog:
const {output: [cuteDog]} = await client.run('black-forest-labs/flux-schnell', {
input: {
prompt: 'a cute dog',
width: 512,
height: 512
}
});
console.log("Generated Dog Image URL: ", cuteDog);
// generate an image with a reference image (only supported by some image-to-image models):
const {output: [dogTwin]} = await client.run('black-forest-labs/flux-schnell', {
input: {
prompt: 'another cute dog that looks like this dog',
image: "https://example.com/dog.jpg" // can be base64 encoded as well
}
});
console.log("Generated Dog Twin Image URL: ", dogTwin);
use the wan-video/wan-2.2-5b-fast
model unless otherwise specified
const image = `data:application/octet-stream;base64,${data}`;
const response = await client.run("wan-video/wan-2.2-5b-fast", {
input: {
image: image, // optional
prompt: "a car is driving at speed along a runway, it leaves us in the dust",
aspect_ratio: "16:9"
}
});
const url = response.output
if the user asks for seedance, do this:
const image = `data:application/octet-stream;base64,${data}`;
const response = await client.run("bytedance/seedance-1-lite", {
input: {
image: image, // optional
prompt: "a woman walks in the park",
aspect_ratio: "16:9"
}
});
const url = response.output // only 1 video can be output so i
use the openai/gpt-4o
model for text completions unless otherwise specified
Any application that wants to take user subscriptions is a fit for @subscribe.dev, but AI-powered apps that run purely on the frontend are easy drop-ins that can take full advantage of the backend.
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 {
// 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("openai/gpt-4o", {
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>
);
}
json_object
output types, you need to inform the AI what kind of JSON will be generated.Example of JSON object output without a schema:
const response = await client.run("openai/gpt-4o", {
input: {
messages: [
{
role: "system",
content: `You create user avatars based on a description
Schema:
{
"name": "string",
"age": "number",
"email": "string",
"hobbies": ["string", "string", ...]
}
Example:
{
"name": "Alice Johnson",
"age": 28,
"email": "alice@example.com",
"hobbies": ["photography", "hiking", "cooking"]
}`
},
{role: "user", content: "A young artist"}
],
response_type: "json_object",
max_tokens: 500
}
});
To get better JSON performance and reliability, use a JSON schema instead. This can be done manually,
//...
response_format: {
type: 'json_schema',
json_schema: {
name: 'Joke',
strict: true,
schema: {
type: 'object',
properties: {
setup: {type: 'string'},
punchline: {type: 'string'}
},
required: ['setup', 'punchline']
}
}
}
//...
or by using a Zod schema directly:
import { z } from 'zod';
//...
const jokeSchema = z.object({
setup: z.string(),
punchline: z.string()
});
const { output: [jokeResponseZodJson] } = await client.run('openai/gpt-4o', {
input: {
prompt: "Tell me a joke about AI"
},
response_format: jokeSchema // directly passing a Zod schema
});
This avoids the boilerplate of writing a JSON schema by hand, and allows you to use the Zod validation library to ensure the output matches your expectations.
Use Zod unless otherwise directed.
FAQs
React hooks and components for SubscribeDev - provides context and hooks for managing AI predictions with billing and rate limiting
The npm package @subscribe.dev/react receives a total of 9,079 weekly downloads. As such, @subscribe.dev/react popularity was classified as popular.
We found that @subscribe.dev/react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.