SDKs & Integrations
React SDK
Hooks, components, and TypeScript types for building Outfame dashboards in React. Tree-shakeable, 12.4 KB gzipped.
| Feature | Details |
|---|---|
| Package | @outfame/react |
| Version | 2.6.0 |
| React | ≥ 18.0 |
| TypeScript | ≥ 5.0 |
| Bundle size | 12.4 KB gzipped (tree-shakeable) |
| License | MIT |
| Source | github.com/outfame/outfame-react |
Installation
npm install @outfame/react
# or
yarn add @outfame/react
# or
pnpm add @outfame/react
# or
bun add @outfame/reactProvider setup
Wrap your app with OutfameProvider to initialize auth, caching, and real-time updates.
import { OutfameProvider } from "@outfame/react";
function App() {
return (
<OutfameProvider
apiKey="sk_live_your_api_key"
options={{
// Cache growth data for 60 seconds
cacheTtl: 60000,
// Automatically refresh follower counts
revalidateOnFocus: true,
// Enable real-time WebSocket updates for live growth tracking
realtime: true,
}}
>
<YourApp />
</OutfameProvider>
);
}Security — Never expose your secret API key in client-side code. Use a proxy endpoint on your server to relay requests. See the Authentication guide for the recommended pattern.
Hooks
Four core hooks for fetching account, analytics, targeting, and engagement data. Each handles loading, errors, caching, and revalidation.
useAccount
Fetch account details and status.
import { useAccount } from "@outfame/react";
function AccountHeader({ accountId }: { accountId: string }) {
const { data: account, isLoading, error, mutate } = useAccount(accountId);
if (isLoading) return <Skeleton />;
if (error) return <ErrorDisplay error={error} />;
return (
<div className="flex items-center gap-4">
<img
src={account.avatar_url}
alt={account.instagram_username}
className="h-16 w-16 rounded-full"
/>
<div>
<h2 className="text-xl font-bold">@{account.instagram_username}</h2>
<p className="text-gray-500">
{account.followers_count.toLocaleString()} followers
</p>
<span className={`badge ${
account.status === "active" ? "badge-green" : "badge-yellow"
}`}>
{account.status === "active" ? "Growing" : "Paused"}
</span>
</div>
</div>
);
}useAnalytics
Growth metrics, engagement rates, and audience demographics with time range control.
import { useAnalytics } from "@outfame/react";
function GrowthMetrics({ accountId }: { accountId: string }) {
const {
data: analytics,
isLoading,
period,
setPeriod,
} = useAnalytics(accountId, {
period: "30d",
granularity: "daily",
});
if (isLoading) return <MetricsSkeleton />;
return (
<div>
<div className="grid grid-cols-4 gap-6">
<Metric label="Net Growth" value={`+${analytics.net_growth}`} />
<Metric label="Followers Gained" value={analytics.followers_gained} />
<Metric label="Engagement Rate" value={`${analytics.engagement_rate}%`} />
<Metric label="Follow-back Rate" value={`${analytics.follow_back_rate}%`} />
</div>
{/* Growth trend — shows real follower gains over time */}
<div className="mt-6">
{analytics.data_points.map((point) => (
<div key={point.date} className="flex justify-between">
<span>{point.date}</span>
<span className="font-medium">+{point.net} followers</span>
</div>
))}
</div>
</div>
);
}useTargeting
Read and update targeting config — competitors, hashtags, locations, and AI optimization settings.
import { useTargeting } from "@outfame/react";
function TargetingEditor({ accountId }: { accountId: string }) {
const {
data: config,
update,
suggestions,
isUpdating,
} = useTargeting(accountId);
const handleAddCompetitor = async (username: string) => {
await update({
competitor_accounts: [...config.competitor_accounts, username],
});
};
const handleApplySuggestions = async () => {
const aiSuggestions = await suggestions({ goal: "growth" });
await update({
competitor_accounts: aiSuggestions.add_competitors.map((s) => s.username),
hashtags: aiSuggestions.add_hashtags.map((s) => s.tag),
ai_optimization: {
enabled: true,
mode: "balanced",
quality_threshold: 0.75,
},
});
};
return (
<div className="space-y-6">
<section>
<h3>Competitor Accounts</h3>
<p className="text-sm text-gray-500">
Followers of these accounts will be targeted.
</p>
<div className="flex flex-wrap gap-2">
{config.competitor_accounts.map((username) => (
<span key={username} className="rounded-full bg-gray-100 px-3 py-1">
@{username}
</span>
))}
</div>
</section>
<button onClick={handleApplySuggestions} disabled={isUpdating}>
{isUpdating ? "Applying..." : "Apply AI Suggestions"}
</button>
</div>
);
}useEngagement
Live engagement activity, daily limits, and pause/resume controls.
import { useEngagement } from "@outfame/react";
function EngagementPanel({ accountId }: { accountId: string }) {
const {
stats,
activity,
pause,
resume,
isActive,
isLoading,
} = useEngagement(accountId, {
refreshInterval: 10000, // Poll every 10s for live updates
});
if (isLoading) return <EngagementSkeleton />;
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3>Growth Engine</h3>
<button
onClick={() => (isActive ? pause("Manual pause") : resume())}
className={isActive ? "bg-yellow-500" : "bg-emerald-500"}
>
{isActive ? "Pause" : "Resume"}
</button>
</div>
{/* Today's engagement stats */}
<div className="grid grid-cols-3 gap-4">
<div>
<p className="text-2xl font-bold">
{stats.actions.likes.performed}/{stats.actions.likes.limit}
</p>
<p className="text-sm text-gray-500">Likes today</p>
</div>
<div>
<p className="text-2xl font-bold">
{stats.actions.follows.performed}/{stats.actions.follows.limit}
</p>
<p className="text-sm text-gray-500">Follows today</p>
</div>
<div>
<p className="text-2xl font-bold">
{(stats.conversion_rates.follow_to_follow_back * 100).toFixed(1)}%
</p>
<p className="text-sm text-gray-500">Follow-back rate</p>
</div>
</div>
{/* Recent activity feed */}
<div>
<h4>Recent Activity</h4>
{activity.map((act) => (
<div key={act.id} className="flex items-center gap-3 py-2">
<span className="text-sm">{act.type}</span>
<span className="font-medium">@{act.target_username}</span>
<span className="ml-auto text-xs text-gray-400">
{new Date(act.created_at).toLocaleTimeString()}
</span>
</div>
))}
</div>
</div>
);
}Pre-built components
Drop-in components with className overrides and render props.
FollowerGrowthChart
import { FollowerGrowthChart } from "@outfame/react";
<FollowerGrowthChart
accountId="acc_7Gx2kLm9Qr"
period="30d"
height={320}
showTrend // Overlay trend line
showAnnotations // Mark milestones (1K, 5K, 10K, etc.)
showTooltip // Hover tooltips with daily breakdown
colors={{
primary: "#ec4899", // Line color
gradient: ["#ec489920", "transparent"],
milestone: "#10b981",
}}
className="rounded-xl border"
/>EngagementStats
import { EngagementStats } from "@outfame/react";
<EngagementStats
accountId="acc_7Gx2kLm9Qr"
layout="grid" // "grid" | "list" | "compact"
showConversionRates // Display follow-back rates
showQualityScores // Show AI quality scores per action
period="7d"
/>TargetingPanel
import { TargetingPanel } from "@outfame/react";
<TargetingPanel
accountId="acc_7Gx2kLm9Qr"
editable // Allow inline editing
showSuggestions // Show targeting recommendations
onUpdate={(config) => console.log("Targeting updated:", config)}
/>CampaignManager
import { CampaignManager } from "@outfame/react";
<CampaignManager
accountId="acc_7Gx2kLm9Qr"
showHistory // Display past campaigns
showSchedule // Show upcoming engagement schedule
allowPause // Enable pause/resume controls
onStatusChange={(status) => console.log("Campaign:", status)}
/>Custom dashboard
Hooks and components combined into a working dashboard. Adapt to your design system.
import {
OutfameProvider,
useAccount,
useAnalytics,
useEngagement,
FollowerGrowthChart,
EngagementStats,
} from "@outfame/react";
function InstagramGrowthDashboard({ accountId }: { accountId: string }) {
const { data: account } = useAccount(accountId);
const { data: analytics } = useAnalytics(accountId, { period: "30d" });
const { stats, isActive, pause, resume } = useEngagement(accountId);
if (!account || !analytics) return <DashboardSkeleton />;
return (
<div className="mx-auto max-w-6xl space-y-8 p-8">
{/* Account header */}
<header className="flex items-center justify-between">
<div className="flex items-center gap-4">
<img
src={account.avatar_url}
alt={account.instagram_username}
className="h-14 w-14 rounded-full"
/>
<div>
<h1 className="text-2xl font-bold">
@{account.instagram_username}
</h1>
<p className="text-gray-500">
{account.followers_count.toLocaleString()} followers
</p>
</div>
</div>
<button
onClick={() => isActive ? pause() : resume()}
className={`rounded-lg px-4 py-2 font-medium ${
isActive
? "bg-emerald-50 text-emerald-700"
: "bg-gray-100 text-gray-700"
}`}
>
{isActive ? "Growing" : "Paused"}
</button>
</header>
{/* Key metrics */}
<div className="grid grid-cols-4 gap-6">
<MetricCard
label="Net Growth"
value={`+${analytics.net_growth.toLocaleString()}`}
description="Followers gained"
/>
<MetricCard
label="Engagement Rate"
value={`${analytics.engagement_rate}%`}
description="Avg across all content"
/>
<MetricCard
label="Quality Score"
value={`${analytics.audience_quality.score}/100`}
description="Follower quality score"
/>
<MetricCard
label="Follow-back Rate"
value={`${(stats.conversion_rates.follow_to_follow_back * 100).toFixed(1)}%`}
description="Users who followed back"
/>
</div>
{/* Growth chart */}
<section className="rounded-2xl border p-6">
<h2 className="mb-4 text-lg font-semibold">Follower Growth</h2>
<FollowerGrowthChart
accountId={accountId}
period="30d"
height={360}
showTrend
showAnnotations
/>
</section>
{/* Engagement breakdown */}
<section className="rounded-2xl border p-6">
<h2 className="mb-4 text-lg font-semibold">Engagement Overview</h2>
<EngagementStats
accountId={accountId}
layout="grid"
showConversionRates
period="30d"
/>
</section>
</div>
);
}
function MetricCard({ label, value, description }: {
label: string;
value: string;
description: string;
}) {
return (
<div className="rounded-xl border p-5">
<p className="text-sm text-gray-500">{label}</p>
<p className="mt-1 text-3xl font-bold">{value}</p>
<p className="mt-1 text-xs text-gray-400">{description}</p>
</div>
);
}
// Wrap with provider at your app root
export default function App() {
return (
<OutfameProvider apiKey="sk_live_your_api_key">
<InstagramGrowthDashboard accountId="acc_7Gx2kLm9Qr" />
</OutfameProvider>
);
}TypeScript support
Every hook and component is typed. All types are exported.
import type {
Account,
AnalyticsOverview,
GrowthDataPoint,
TargetingConfig,
TargetingSuggestions,
EngagementStats,
EngagementActivity,
AudienceQuality,
CampaignStatus,
OutfameError,
} from "@outfame/react";
// All hooks return typed data
const { data } = useAccount("acc_7Gx2kLm9Qr");
// ^? Account | undefined
const { data: analytics } = useAnalytics("acc_7Gx2kLm9Qr", { period: "30d" });
// ^? AnalyticsOverview | undefined
// Component props are fully typed
<FollowerGrowthChart
accountId="acc_7Gx2kLm9Qr"
period="30d" // "7d" | "30d" | "90d" | "1y" | "custom"
granularity="daily" // "hourly" | "daily" | "weekly" | "monthly"
height={320} // number
showTrend // boolean
/>Generic hooks
import { useOutfame } from "@outfame/react";
// Custom typed request
interface CustomMetrics {
totalReach: number;
viralPosts: number;
topHashtags: string[];
}
const { data } = useOutfame<CustomMetrics>(
`/accounts/acc_7Gx2kLm9Qr/custom-metrics`,
{ period: "30d" }
);
// data is typed as CustomMetrics | undefinedState management
Uses an internal SWR-like cache. Works alongside Redux, Zustand, Jotai, TanStack Query, or plain Context.
| Library | Compatibility | Pattern |
|---|---|---|
| Redux / Redux Toolkit | Full | Use hooks inside components, dispatch to store as needed |
| Zustand | Full | Sync hook data to Zustand store via useEffect |
| React Context | Full | Nest OutfameProvider inside your context providers |
| Jotai | Full | Derive atoms from hook return values |
| TanStack Query | Full | Use outfame.sdk directly inside queryFn |
Zustand example
import { create } from "zustand";
import { useAnalytics } from "@outfame/react";
import { useEffect } from "react";
interface GrowthStore {
netGrowth: number;
engagementRate: number;
setMetrics: (netGrowth: number, engagementRate: number) => void;
}
const useGrowthStore = create<GrowthStore>((set) => ({
netGrowth: 0,
engagementRate: 0,
setMetrics: (netGrowth, engagementRate) =>
set({ netGrowth, engagementRate }),
}));
// Sync Outfame data into Zustand
function useGrowthSync(accountId: string) {
const { data } = useAnalytics(accountId, { period: "30d" });
const setMetrics = useGrowthStore((s) => s.setMetrics);
useEffect(() => {
if (data) {
setMetrics(data.net_growth, data.engagement_rate);
}
}, [data, setMetrics]);
}Configuration
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Your Outfame API key or proxy token. |
options.cacheTtl | number | 60000 | Cache time-to-live in milliseconds. |
options.revalidateOnFocus | boolean | true | Refetch when window regains focus. |
options.realtime | boolean | false | Enable WebSocket for live follower updates. |
options.baseUrl | string | https://api.outfame.com/v1 | API base URL. Override for proxy setups. |
options.errorRetryCount | number | 3 | Number of retries on transient errors. |
Next steps — Next.js integration for SSR | Growth guide