React UI Components
Integrate Outstand into your React app with @outstand-so/ui, a library of prebuilt social media components and hooks.
@outstand-so/ui is a React component library that provides pre-built UI components and hooks for integrating Outstand's social media API into your React applications. It simplifies the process of building social media management interfaces by providing ready-to-use components for account management, post creation, media uploads, and analytics.
Installation
Install the package from npm:
npm install @outstand-so/uiOr with yarn:
yarn add @outstand-so/uiOr with pnpm:
pnpm add @outstand-so/uiGetting Started
Provider Setup
Wrap your application (or the part that uses Outstand components) with the OutstandProvider:
import { OutstandProvider } from '@outstand-so/ui';
function App() {
return (
<OutstandProvider apiKey="your-api-key" baseUrl="https://api.outstand.so">
{/* Your app components */}
</OutstandProvider>
);
}Props:
apiKey(required): Your Outstand API keybaseUrl(optional): Base URL for API requests. Defaults tohttps://api.outstand.sotenantId(optional): Tenant ID for multi-tenant applicationsinitialAccounts(optional): Pre-populate accounts without fetching
Components
Account Management
ConnectAccountButton
A button component that initiates OAuth flow for connecting a social media account.
import { ConnectAccountButton } from '@outstand-so/ui';
<ConnectAccountButton
network="x"
redirectUri="https://yourapp.com/callback"
apiKey="your-api-key"
onSuccess={(authUrl) => {
window.location.href = authUrl;
}}
onError={(error) => {
console.error('Connection error:', error);
}}
/>Props:
network(required): Social network to connect ("threads" | "bluesky" | "x" | "linkedin" | "youtube" | "instagram" | "facebook" | "tiktok" | "pinterest")redirectUri(required): URL to redirect after OAuth flowapiKey(required): Your API keytenantId(optional): Tenant ID for multi-tenant appsbaseUrl(optional): API base URLonSuccess(optional): Callback when auth URL is obtainedonError(optional): Error handlerclassName(optional): Additional CSS classeschildren(optional): Custom button contentvariant(optional): Button style ("default" | "outline" | "ghost")size(optional): Button size ("sm" | "md" | "lg")
ConnectAccountButtonGroup
Display multiple connection buttons in a group layout.
import { ConnectAccountButtonGroup } from '@outstand-so/ui';
<ConnectAccountButtonGroup
networks={['x', 'linkedin', 'instagram', 'facebook']}
redirectUri="https://yourapp.com/callback"
apiKey="your-api-key"
layout="grid"
variant="outline"
/>Props:
networks(required): Array of social networks to showredirectUri(required): OAuth callback URLapiKey(required): Your API keylayout(optional): Layout style ("horizontal" | "vertical" | "grid")- Other props same as
ConnectAccountButton
AccountsList
Display a paginated list of connected social accounts with management options.
import { AccountsList } from '@outstand-so/ui';
<AccountsList
apiKey="your-api-key"
onAccountSelect={(account) => {
console.log('Selected:', account);
}}
onAccountDisconnect={(accountId) => {
console.log('Disconnected:', accountId);
}}
pageSize={20}
/>Props:
apiKey(required): Your API keybaseUrl(optional): API base URLtenantId(optional): Tenant IDonAccountSelect(optional): Callback when account is selectedonAccountDisconnect(optional): Callback when account is disconnectedclassName(optional): Additional CSS classespageSize(optional): Number of accounts per page (default: 20)
AccountCard
Display a single social account card.
import { AccountCard } from '@outstand-so/ui';
<AccountCard
account={account}
onSelect={() => handleSelect(account)}
onDisconnect={() => handleDisconnect(account.id)}
/>Props:
account(required): SocialAccount objectonSelect(optional): Selection handleronDisconnect(optional): Disconnect handlerclassName(optional): Additional CSS classes
Post Creation
CreatePostForm
A complete form component for creating and scheduling posts.
import { CreatePostForm } from '@outstand-so/ui';
<CreatePostForm
apiKey="your-api-key"
enableScheduling={true}
enableDrafts={true}
onPostCreated={(post) => {
console.log('Post created:', post);
}}
onError={(error) => {
console.error('Error:', error);
}}
/>Props:
apiKey(required): Your API keybaseUrl(optional): API base URLaccounts(optional): Pre-selected accountsonPostCreated(optional): Callback when post is createdonError(optional): Error handlerclassName(optional): Additional CSS classesenableScheduling(optional): Enable scheduling UI (default: false)enableDrafts(optional): Enable draft saving (default: false)
PostComposer
A rich text editor component for composing post content with character limits and account-specific validation.
import { PostComposer } from '@outstand-so/ui';
<PostComposer
value={postContent}
onChange={setPostContent}
selectedAccounts={selectedAccounts}
placeholder="What's on your mind?"
maxLength={5000}
/>Props:
value(required): Current post contentonChange(required): Content change handlerselectedAccounts(optional): Selected accounts for validationplaceholder(optional): Placeholder textmaxLength(optional): Maximum character lengthclassName(optional): Additional CSS classes
NetworkSelector
Component for selecting which social accounts to post to.
import { NetworkSelector } from '@outstand-so/ui';
<NetworkSelector
accounts={accounts}
selectedIds={selectedAccountIds}
onChange={setSelectedAccountIds}
/>Props:
accounts(required): Array of SocialAccount objectsselectedIds(required): Array of selected account IDsonChange(required): Selection change handlerclassName(optional): Additional CSS classes
PostScheduler
Date and time picker for scheduling posts.
import { PostScheduler } from '@outstand-so/ui';
<PostScheduler
value={scheduledDate}
onChange={setScheduledDate}
minDate={new Date()}
maxDays={30}
/>Props:
value(required): Selected date (Date | null)onChange(required): Date change handlerclassName(optional): Additional CSS classesminDate(optional): Minimum selectable datemaxDays(optional): Maximum days in the future
NetworkConfigPanel
Configure platform-specific options for selected networks.
import { NetworkConfigPanel } from '@outstand-so/ui';
<NetworkConfigPanel
networks={['instagram', 'youtube', 'tiktok']}
configs={networkConfigs}
onChange={setNetworkConfigs}
/>Props:
networks(required): Array of network namesconfigs(required): Configuration objectonChange(required): Config change handlerclassName(optional): Additional CSS classes
Configuration Options:
These match the platform config accepted by Create a post - camelCase, except for threads.reply_control and the Pinterest fields.
- Threads:
{ countries?: string[], reply_control?: "everyone" | "accounts_you_follow" | "mentioned_only" } - Instagram:
{ publishAsStory?: boolean, reelThumbOffset?: number, altText?: string, collaborators?: string[] } - YouTube:
{ title?: string, isShort?: boolean, privacyStatus?: "public" | "private" | "unlisted", categoryId?: string, madeForKids?: boolean, tags?: string[] } - TikTok:
{ postMode?: "MEDIA_UPLOAD" | "DIRECT_POST", privacyLevel?: "PUBLIC_TO_EVERYONE" | "MUTUAL_FOLLOW_FRIENDS" | "FOLLOWER_OF_CREATOR" | "SELF_ONLY", disableDuet?: boolean, disableStitch?: boolean, disableComment?: boolean } - Facebook:
{ publishAsStory?: boolean, publishAsReel?: boolean, mentions?: { pageId: string, text: string }[] }
TikTok defaults to
postMode: "MEDIA_UPLOAD", which delivers the media to the creator's TikTok inbox as a draft. SetpostMode: "DIRECT_POST"with aprivacyLevelto publish straight to the profile.
Media Management
MediaUploader
Component for uploading images and videos.
import { MediaUploader } from '@outstand-so/ui';
<MediaUploader
apiKey="your-api-key"
onUploadComplete={(media) => {
console.log('Uploaded:', media);
}}
onUploadError={(error) => {
console.error('Upload error:', error);
}}
maxFiles={10}
acceptedTypes={['image/*', 'video/*']}
/>Props:
apiKey(required): Your API keybaseUrl(optional): API base URLonUploadComplete(optional): Callback with uploaded media filesonUploadError(optional): Error handlermaxFiles(optional): Maximum number of files (default: unlimited)acceptedTypes(optional): MIME types to acceptclassName(optional): Additional CSS classes
MediaPreview
Preview a single media file with optional remove action.
import { MediaPreview } from '@outstand-so/ui';
<MediaPreview
media={mediaFile}
onRemove={() => handleRemove(mediaFile.id)}
progress={uploadProgress}
/>Props:
media(required): MediaFile objectonRemove(optional): Remove handlerprogress(optional): Upload progress (0-100)className(optional): Additional CSS classes
MediaGallery
Display multiple media files in a grid layout.
import { MediaGallery } from '@outstand-so/ui';
<MediaGallery
media={mediaFiles}
onRemove={(index) => {
setMediaFiles(prev => prev.filter((_, i) => i !== index));
}}
columns={3}
/>Props:
media(required): Array of MediaFile objectsonRemove(optional): Remove handler with indexclassName(optional): Additional CSS classescolumns(optional): Grid columns (2 | 3 | 4, default: 3)
Analytics
PostMetrics
Display analytics for a specific post.
import { PostMetrics } from '@outstand-so/ui';
<PostMetrics
apiKey="your-api-key"
postId="post-id"
refreshInterval={30000}
/>Props:
apiKey(required): Your API keypostId(required): Post ID to display metrics forbaseUrl(optional): API base URLclassName(optional): Additional CSS classesrefreshInterval(optional): Auto-refresh interval in milliseconds
PostMetricsCard
Display metrics for a single account's post.
import { PostMetricsCard } from '@outstand-so/ui';
<PostMetricsCard
account={account}
metrics={metricsData}
platformPostId="123456789"
publishedAt="2025-01-15T10:30:00Z"
/>Props:
account(required): Account object with id, nickname, network, usernamemetrics(required): PostMetricsData objectplatformPostId(optional): Platform-specific post IDpublishedAt(optional): Publication timestampclassName(optional): Additional CSS classes
PostsWithMetrics
Display a paginated list of posts with their metrics.
import { PostsWithMetrics } from '@outstand-so/ui';
<PostsWithMetrics
apiKey="your-api-key"
socialAccountId="account-id"
pageSize={10}
/>Props:
apiKey(required): Your API keybaseUrl(optional): API base URLsocialAccountId(optional): Filter by account IDpageSize(optional): Posts per page (default: 10)className(optional): Additional CSS classes
OAuth Flow
OAuthCallback
Handle OAuth callback and finalize account connections.
import { OAuthCallback } from '@outstand-so/ui';
<OAuthCallback
apiKey="your-api-key"
onSuccess={(accounts) => {
console.log('Connected accounts:', accounts);
}}
onError={(error) => {
console.error('Connection error:', error);
}}
/>Props:
apiKey(required): Your API keybaseUrl(optional): API base URLonSuccess(optional): Callback with connected accountsonError(optional): Error handlerclassName(optional): Additional CSS classes
PageSelector
Select pages to connect (for networks like Facebook that have multiple pages).
import { PageSelector } from '@outstand-so/ui';
<PageSelector
network="facebook"
pages={availablePages}
onSelect={(selectedIds) => {
console.log('Selected pages:', selectedIds);
}}
isLoading={false}
/>Props:
network(required): Network namepages(required): Array of available pagesonSelect(required): Selection handlerisLoading(optional): Loading stateclassName(optional): Additional CSS classes
Hooks
useOutstand
Access the Outstand context (accounts, selected accounts, loading state).
import { useOutstand } from '@outstand-so/ui';
function MyComponent() {
const { accounts, selectedAccounts, setSelectedAccounts, isLoading } = useOutstand();
return (
<div>
{accounts.map(account => (
<div key={account.id}>{account.username}</div>
))}
</div>
);
}Returns:
apiKey: Current API keybaseUrl: Base URLtenantId: Tenant IDaccounts: Array of SocialAccountsetAccounts: Update accountsselectedAccounts: Array of selected account IDssetSelectedAccounts: Update selected accountsisLoading: Loading statesetIsLoading: Update loading state
useOutstandApi
Get API client methods for making requests.
import { useOutstandApi } from '@outstand-so/ui';
function MyComponent() {
const api = useOutstandApi({ apiKey: 'your-api-key' });
const fetchAccounts = async () => {
const response = await api.get('/v1/social-accounts');
if (response.success) {
console.log('Accounts:', response.data);
}
};
return <button onClick={fetchAccounts}>Fetch Accounts</button>;
}Returns:
get<T>(endpoint): GET requestpost<T>(endpoint, body): POST requestpatch<T>(endpoint, body): PATCH requestdelete<T>(endpoint): DELETE requestrequest<T>(endpoint, options): Custom request
useAccounts
Fetch and manage social accounts with pagination.
import { useAccounts } from '@outstand-so/ui';
function AccountsComponent() {
const {
accounts,
total,
isLoading,
error,
refetch,
disconnectAccount,
hasNextPage,
hasPrevPage
} = useAccounts({
apiKey: 'your-api-key',
limit: 20,
offset: 0
});
return (
<div>
{accounts.map(account => (
<div key={account.id}>
{account.username}
<button onClick={() => disconnectAccount(account.id)}>
Disconnect
</button>
</div>
))}
</div>
);
}Options:
apiKey(required): Your API keybaseUrl(optional): API base URLtenantId(optional): Tenant IDlimit(optional): Page sizeoffset(optional): Pagination offset
Returns:
accounts: Array of SocialAccounttotal: Total countisLoading: Loading stateerror: Error objectrefetch: Refetch functiondisconnectAccount: Disconnect functionhasNextPage: BooleanhasPrevPage: Boolean
usePosts
Fetch and manage posts with pagination.
import { usePosts } from '@outstand-so/ui';
function PostsComponent() {
const {
posts,
total,
isLoading,
error,
refetch,
createPost,
deletePost,
hasNextPage,
hasPrevPage
} = usePosts({
apiKey: 'your-api-key',
socialAccountId: 'account-id',
limit: 10
});
const handleCreate = async () => {
const response = await createPost({
content: 'Hello world!',
accounts: ['account-id']
});
if (response.success) {
console.log('Post created:', response.data);
}
};
return (
<div>
<button onClick={handleCreate}>Create Post</button>
{posts.map(post => (
<div key={post.id}>
{post.containers[0]?.content}
<button onClick={() => deletePost(post.id)}>Delete</button>
</div>
))}
</div>
);
}Options:
apiKey(required): Your API keybaseUrl(optional): API base URLsocialAccountId(optional): Filter by accountlimit(optional): Page sizeoffset(optional): Pagination offset
Returns:
posts: Array of Posttotal: Total countisLoading: Loading stateerror: Error objectrefetch: Refetch functioncreatePost: Create post functiondeletePost: Delete post functionhasNextPage: BooleanhasPrevPage: Boolean
usePostMetrics
Fetch analytics for a specific post.
import { usePostMetrics } from '@outstand-so/ui';
function MetricsComponent({ postId }: { postId: string }) {
const {
analytics,
isLoading,
error,
refetch
} = usePostMetrics({
apiKey: 'your-api-key',
postId,
refreshInterval: 30000
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<p>Total Likes: {analytics?.aggregated_metrics.total_likes}</p>
<p>Total Views: {analytics?.aggregated_metrics.total_views}</p>
</div>
);
}Options:
apiKey(required): Your API keypostId(required): Post IDbaseUrl(optional): API base URLrefreshInterval(optional): Auto-refresh interval in ms
Returns:
analytics: PostAnalytics object or nullisLoading: Loading stateerror: Error objectrefetch: Refetch function
useMediaUpload
Upload media files to Outstand.
import { useMediaUpload } from '@outstand-so/ui';
function UploadComponent() {
const {
uploadFile,
uploadFiles,
getUploadUrl,
isUploading,
uploadProgress
} = useMediaUpload({
apiKey: 'your-api-key'
});
const handleUpload = async (file: File) => {
const mediaFile = await uploadFile(file);
if (mediaFile) {
console.log('Uploaded:', mediaFile);
}
};
return (
<div>
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) handleUpload(file);
}}
/>
{isUploading && <div>Uploading... {uploadProgress[file.name]}%</div>}
</div>
);
}Options:
apiKey(required): Your API keybaseUrl(optional): API base URL
Returns:
uploadFile: Upload single fileuploadFiles: Upload multiple filesgetUploadUrl: Get upload URLisUploading: Uploading stateuploadProgress: Progress by filename
useOAuthFlow
Manage OAuth flow for connecting accounts.
getPendingConnection and finalizeConnection are authenticated with your API key, and this hook
runs in the browser. Use them here only where the key is already scoped to a context you are happy
to expose; otherwise proxy both calls through your own backend and keep the key server-side.
getAuthUrl is the only part of this hook that belongs in an untrusted page.
import { useOAuthFlow } from '@outstand-so/ui';
function OAuthComponent() {
const {
getAuthUrl,
getPendingConnection,
finalizeConnection,
isLoading
} = useOAuthFlow({
apiKey: 'your-api-key'
});
const handleConnect = async () => {
const response = await getAuthUrl('x', 'https://yourapp.com/callback');
if (response.success && response.data) {
window.location.href = response.data.auth_url;
}
};
return (
<button onClick={handleConnect} disabled={isLoading}>
Connect X Account
</button>
);
}Options:
apiKey(required): Your API keybaseUrl(optional): API base URL
Returns:
getAuthUrl: Get OAuth URLgetPendingConnection: Get pending connection detailsfinalizeConnection: Finalize connectionisLoading: Loading state
TypeScript Types
The package exports comprehensive TypeScript types:
import type {
SocialAccount,
SocialNetwork,
Post,
PostContainer,
PostSocialAccount,
MediaFile,
PostAnalytics,
PostMetricsData,
CreatePostRequest,
ApiResponse,
PaginatedResponse,
// ... and more
} from '@outstand-so/ui';Key Types:
SocialNetwork: Union type of supported networksSocialAccount: Account object structurePost: Post object structureMediaFile: Media file structurePostAnalytics: Analytics data structureCreatePostRequest: Post creation payloadApiResponse<T>: Standard API response wrapper
Examples
Complete Post Creation Flow
import {
OutstandProvider,
CreatePostForm,
AccountsList
} from '@outstand-so/ui';
function PostManager() {
return (
<OutstandProvider apiKey="your-api-key">
<div>
<h2>Connected Accounts</h2>
<AccountsList apiKey="your-api-key" />
<h2>Create Post</h2>
<CreatePostForm
apiKey="your-api-key"
enableScheduling={true}
onPostCreated={(post) => {
alert(`Post created: ${post.id}`);
}}
/>
</div>
</OutstandProvider>
);
}Custom Post Creation with Individual Components
import {
OutstandProvider,
PostComposer,
NetworkSelector,
MediaUploader,
PostScheduler,
NetworkConfigPanel,
useAccounts
} from '@outstand-so/ui';
function CustomPostCreator() {
const { accounts } = useAccounts({ apiKey: 'your-api-key' });
const [content, setContent] = useState('');
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [media, setMedia] = useState<MediaFile[]>([]);
const [scheduledAt, setScheduledAt] = useState<Date | null>(null);
const handleSubmit = async () => {
// Use usePosts hook to create post
// ...
};
return (
<div>
<PostComposer
value={content}
onChange={setContent}
selectedAccounts={accounts.filter(a => selectedIds.includes(a.id))}
/>
<NetworkSelector
accounts={accounts}
selectedIds={selectedIds}
onChange={setSelectedIds}
/>
<MediaUploader
apiKey="your-api-key"
onUploadComplete={setMedia}
/>
<PostScheduler
value={scheduledAt}
onChange={setScheduledAt}
/>
<NetworkConfigPanel
networks={[...new Set(accounts.filter(a => selectedIds.includes(a.id)).map(a => a.network))]}
configs={{}}
onChange={() => {}}
/>
<button onClick={handleSubmit}>Create Post</button>
</div>
);
}View Post Analytics
import { PostMetrics, PostsWithMetrics } from '@outstand-so/ui';
function AnalyticsDashboard() {
const [selectedPostId, setSelectedPostId] = useState<string | null>(null);
return (
<div>
{selectedPostId ? (
<PostMetrics
apiKey="your-api-key"
postId={selectedPostId}
refreshInterval={30000}
/>
) : (
<PostsWithMetrics
apiKey="your-api-key"
pageSize={10}
/>
)}
</div>
);
}Learn More
- Check out the playground example to see all components in action
- View the package on npm