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/ui

Or with yarn:

yarn add @outstand-so/ui

Or with pnpm:

pnpm add @outstand-so/ui

Getting 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 key
  • baseUrl (optional): Base URL for API requests. Defaults to https://api.outstand.so
  • tenantId (optional): Tenant ID for multi-tenant applications
  • initialAccounts (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 flow
  • apiKey (required): Your API key
  • tenantId (optional): Tenant ID for multi-tenant apps
  • baseUrl (optional): API base URL
  • onSuccess (optional): Callback when auth URL is obtained
  • onError (optional): Error handler
  • className (optional): Additional CSS classes
  • children (optional): Custom button content
  • variant (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 show
  • redirectUri (required): OAuth callback URL
  • apiKey (required): Your API key
  • layout (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 key
  • baseUrl (optional): API base URL
  • tenantId (optional): Tenant ID
  • onAccountSelect (optional): Callback when account is selected
  • onAccountDisconnect (optional): Callback when account is disconnected
  • className (optional): Additional CSS classes
  • pageSize (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 object
  • onSelect (optional): Selection handler
  • onDisconnect (optional): Disconnect handler
  • className (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 key
  • baseUrl (optional): API base URL
  • accounts (optional): Pre-selected accounts
  • onPostCreated (optional): Callback when post is created
  • onError (optional): Error handler
  • className (optional): Additional CSS classes
  • enableScheduling (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 content
  • onChange (required): Content change handler
  • selectedAccounts (optional): Selected accounts for validation
  • placeholder (optional): Placeholder text
  • maxLength (optional): Maximum character length
  • className (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 objects
  • selectedIds (required): Array of selected account IDs
  • onChange (required): Selection change handler
  • className (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 handler
  • className (optional): Additional CSS classes
  • minDate (optional): Minimum selectable date
  • maxDays (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 names
  • configs (required): Configuration object
  • onChange (required): Config change handler
  • className (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. Set postMode: "DIRECT_POST" with a privacyLevel to 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 key
  • baseUrl (optional): API base URL
  • onUploadComplete (optional): Callback with uploaded media files
  • onUploadError (optional): Error handler
  • maxFiles (optional): Maximum number of files (default: unlimited)
  • acceptedTypes (optional): MIME types to accept
  • className (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 object
  • onRemove (optional): Remove handler
  • progress (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 objects
  • onRemove (optional): Remove handler with index
  • className (optional): Additional CSS classes
  • columns (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 key
  • postId (required): Post ID to display metrics for
  • baseUrl (optional): API base URL
  • className (optional): Additional CSS classes
  • refreshInterval (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, username
  • metrics (required): PostMetricsData object
  • platformPostId (optional): Platform-specific post ID
  • publishedAt (optional): Publication timestamp
  • className (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 key
  • baseUrl (optional): API base URL
  • socialAccountId (optional): Filter by account ID
  • pageSize (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 key
  • baseUrl (optional): API base URL
  • onSuccess (optional): Callback with connected accounts
  • onError (optional): Error handler
  • className (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 name
  • pages (required): Array of available pages
  • onSelect (required): Selection handler
  • isLoading (optional): Loading state
  • className (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 key
  • baseUrl: Base URL
  • tenantId: Tenant ID
  • accounts: Array of SocialAccount
  • setAccounts: Update accounts
  • selectedAccounts: Array of selected account IDs
  • setSelectedAccounts: Update selected accounts
  • isLoading: Loading state
  • setIsLoading: 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 request
  • post<T>(endpoint, body): POST request
  • patch<T>(endpoint, body): PATCH request
  • delete<T>(endpoint): DELETE request
  • request<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 key
  • baseUrl (optional): API base URL
  • tenantId (optional): Tenant ID
  • limit (optional): Page size
  • offset (optional): Pagination offset

Returns:

  • accounts: Array of SocialAccount
  • total: Total count
  • isLoading: Loading state
  • error: Error object
  • refetch: Refetch function
  • disconnectAccount: Disconnect function
  • hasNextPage: Boolean
  • hasPrevPage: 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 key
  • baseUrl (optional): API base URL
  • socialAccountId (optional): Filter by account
  • limit (optional): Page size
  • offset (optional): Pagination offset

Returns:

  • posts: Array of Post
  • total: Total count
  • isLoading: Loading state
  • error: Error object
  • refetch: Refetch function
  • createPost: Create post function
  • deletePost: Delete post function
  • hasNextPage: Boolean
  • hasPrevPage: 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 key
  • postId (required): Post ID
  • baseUrl (optional): API base URL
  • refreshInterval (optional): Auto-refresh interval in ms

Returns:

  • analytics: PostAnalytics object or null
  • isLoading: Loading state
  • error: Error object
  • refetch: 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 key
  • baseUrl (optional): API base URL

Returns:

  • uploadFile: Upload single file
  • uploadFiles: Upload multiple files
  • getUploadUrl: Get upload URL
  • isUploading: Uploading state
  • uploadProgress: 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 key
  • baseUrl (optional): API base URL

Returns:

  • getAuthUrl: Get OAuth URL
  • getPendingConnection: Get pending connection details
  • finalizeConnection: Finalize connection
  • isLoading: 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 networks
  • SocialAccount: Account object structure
  • Post: Post object structure
  • MediaFile: Media file structure
  • PostAnalytics: Analytics data structure
  • CreatePostRequest: Post creation payload
  • ApiResponse<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