Requirements
- React Native 0.70 or later
- iOS 14.0+ / Android API 24+
- TypeScript 4.7+ (recommended)
- Expo SDK 49 or later
- Development build (not Expo Go)
Installation
- React Native CLI
- Expo
Install the package:Install iOS pods:
Copy
npm install @shortkit/react-native
Copy
cd ios && pod install
Install the Expo-compatible package:Add the config plugin to your Create a development build:
Copy
npx expo install @shortkit/expo
app.json:app.json
Copy
{
"expo": {
"plugins": ["@shortkit/expo"]
}
}
shortkit requires native code and won’t work in Expo Go. You need to use a development build.
Copy
npx expo prebuild
npx expo run:ios
# or
npx expo run:android
Initialization
Initialize the SDK at your app’s entry point, before rendering:App.tsx
Copy
import { shortkit } from '@shortkit/react-native';
// or for Expo: import { shortkit } from '@shortkit/expo';
// Initialize before rendering
shortkit.initialize({
apiKey: 'pk_live_your_publishable_key',
config: {
feedOrientation: 'vertical',
feedHeight: 'fullscreen',
controls: {
playPause: true,
scrubber: true,
playbackSpeed: {
enabled: true,
options: [1.0, 1.25, 1.5, 2.0]
},
captions: {
enabled: true,
defaultOn: false
},
volume: true,
share: { enabled: true }
},
theme: {
primaryColor: '#FF6600',
controlsTint: '#FFFFFF',
backgroundColor: '#000000'
}
},
environment: 'production'
});
export default function App() {
return (
<NavigationContainer>
{/* Your app navigation */}
</NavigationContainer>
);
}
Displaying the feed
Use theShortkitFeed component:
VideoFeedScreen.tsx
Copy
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { ShortkitFeed } from '@shortkit/react-native';
export function VideoFeedScreen() {
return (
<View style={styles.container}>
<ShortkitFeed style={styles.feed} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
feed: {
flex: 1,
},
});
With filters
SportsFeedScreen.tsx
Copy
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { ShortkitFeed } from '@shortkit/react-native';
export function SportsFeedScreen() {
return (
<View style={styles.container}>
<ShortkitFeed
style={styles.feed}
filter={{
tags: ['sports', 'highlights']
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
feed: {
flex: 1,
},
});
With React Navigation
Copy
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { ShortkitFeed } from '@shortkit/react-native';
const Tab = createBottomTabNavigator();
function VideoTab() {
return <ShortkitFeed style={{ flex: 1 }} />;
}
export function MainTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Videos" component={VideoTab} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
User identity
Associate user identity for personalized feeds:Copy
import { shortkit } from '@shortkit/react-native';
// When user logs in
async function handleLogin(userId: string) {
await authenticateUser();
shortkit.setUserId(userId);
}
// When user logs out
function handleLogout() {
shortkit.clearUserId();
}
Event callbacks
Handle SDK events with callback props:VideoFeedScreen.tsx
Copy
import React from 'react';
import { Share, StyleSheet, View } from 'react-native';
import { ShortkitFeed, Content, ShortkitError } from '@shortkit/react-native';
import { useNavigation } from '@react-navigation/native';
export function VideoFeedScreen() {
const navigation = useNavigation();
const handleShare = async (content: Content) => {
try {
await Share.share({
message: content.shareUrl,
url: content.shareUrl,
});
} catch (error) {
console.error('Share failed:', error);
}
};
const handleAction = (action: string, content: Content) => {
switch (action) {
case 'bookmark':
// Save to bookmarks
break;
case 'readMore':
// Navigate to article
navigation.navigate('Article', { url: content.metadata?.articleUrl });
break;
}
};
const handleError = (error: ShortkitError) => {
console.error('shortkit error:', error.message);
};
const handleMetadata = (content: Content, metadata: Record<string, any>) => {
// Access custom metadata from your CMS
console.log('Content metadata:', metadata);
};
return (
<View style={styles.container}>
<ShortkitFeed
style={styles.feed}
onShareRequested={handleShare}
onActionTapped={handleAction}
onError={handleError}
onMetadataLoaded={handleMetadata}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
feed: { flex: 1 },
});
Deep linking
Handle deep links to open specific content:App.tsx
Copy
import { useEffect } from 'react';
import { Linking } from 'react-native';
import { shortkit } from '@shortkit/react-native';
export default function App() {
useEffect(() => {
// Handle initial URL
Linking.getInitialURL().then(url => {
if (url) handleDeepLink(url);
});
// Handle URL while app is open
const subscription = Linking.addEventListener('url', ({ url }) => {
handleDeepLink(url);
});
return () => subscription.remove();
}, []);
const handleDeepLink = (url: string) => {
// Parse your deep link format
// e.g., yourapp://watch/cnt_abc123
const match = url.match(/watch\/(\w+)/);
if (match) {
const contentId = match[1];
shortkit.openContent(contentId);
}
};
return (
// Your app
);
}
expo-linking:
App.tsx
Copy
import * as Linking from 'expo-linking';
import { shortkit } from '@shortkit/expo';
export default function App() {
const url = Linking.useURL();
useEffect(() => {
if (url) {
const { path } = Linking.parse(url);
if (path?.startsWith('watch/')) {
const contentId = path.replace('watch/', '');
shortkit.openContent(contentId);
}
}
}, [url]);
return (
// Your app
);
}
Entry widget
Create a mini-player widget for other screens:HomeScreen.tsx
Copy
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { ShortkitEntryWidget, Content } from '@shortkit/react-native';
import { useNavigation } from '@react-navigation/native';
export function HomeScreen() {
const navigation = useNavigation();
const handleContentSelected = (content: Content) => {
navigation.navigate('VideoFeed', { startContentId: content.id });
};
return (
<View style={styles.container}>
<Text style={styles.title}>Latest Videos</Text>
<ShortkitEntryWidget
style={styles.widget}
itemCount={5}
autoPlay={true}
onContentSelected={handleContentSelected}
/>
{/* Other home screen content */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 12,
},
widget: {
height: 200,
marginBottom: 24,
},
});
TypeScript types
The SDK includes full TypeScript definitions:types.ts
Copy
import type {
ShortkitConfig,
FeedConfig,
ControlsConfig,
ThemeConfig,
Content,
ShortkitError,
FeedFilter,
} from '@shortkit/react-native';
// Config is fully typed
const config: ShortkitConfig = {
apiKey: 'pk_live_...',
config: {
feedOrientation: 'vertical', // 'vertical' | 'horizontal'
feedHeight: 'fullscreen', // 'fullscreen' | { percentage: number }
controls: {
playPause: true,
scrubber: true,
// ...
},
},
};
// Content type includes all fields
function handleContent(content: Content) {
console.log(content.id);
console.log(content.title);
console.log(content.duration);
console.log(content.thumbnailUrl);
console.log(content.shareUrl);
console.log(content.metadata); // Record<string, any>
}
Hooks
The SDK provides React hooks for common operations:Copy
import { useshortkit, useCurrentContent } from '@shortkit/react-native';
function VideoControls() {
const { isInitialized, userId } = useshortkit();
const { content, isPlaying, currentTime, duration } = useCurrentContent();
if (!isInitialized) {
return <ActivityIndicator />;
}
return (
<View>
<Text>{content?.title}</Text>
<Text>{formatTime(currentTime)} / {formatTime(duration)}</Text>
</View>
);
}
Expo configuration
For Expo projects, configure the plugin inapp.json:
app.json
Copy
{
"expo": {
"plugins": [
[
"@shortkit/expo",
{
"ios": {
"backgroundModes": ["audio"]
},
"android": {
"enableChromecast": true
}
}
]
]
}
}
Debugging
Enable debug mode during development:Copy
shortkit.initialize({
apiKey: 'pk_live_your_publishable_key',
config: { /* ... */ },
environment: __DEV__ ? 'development' : 'production'
});
// View debug logs
shortkit.setLogLevel('verbose');
