React UI Components

@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:

  • Threads: { reply_control?: "everyone" | "accounts_you_follow" | "mentioned_only" }
  • Instagram: { media_type?: "REELS" | "STORIES" | "FEED", share_to_feed?: boolean }
  • YouTube: { title?: string, description?: string, privacy_status?: "public" | "private" | "unlisted", category_id?: string, tags?: string[] }
  • TikTok: { privacy_level?: "PUBLIC_TO_EVERYONE" | "MUTUAL_FOLLOW_FRIENDS" | "SELF_ONLY", disable_duet?: boolean, disable_stitch?: boolean, disable_comment?: boolean }

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.

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