Skip to main content
The shortkit React Native SDK provides a unified API for iOS and Android through native bridges, delivering full feature parity with the native SDKs.

Requirements

  • React Native 0.70 or later
  • iOS 14.0+ / Android API 24+
  • TypeScript 4.7+ (recommended)
For Expo:
  • Expo SDK 49 or later
  • Development build (not Expo Go)

Installation

Install the package:
npm install @shortkit/react-native
Install iOS pods:
cd ios && pod install

Initialization

Initialize the SDK at your app’s entry point, before rendering:
App.tsx
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 the ShortkitFeed component:
VideoFeedScreen.tsx
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
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

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:
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
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
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
  );
}
For Expo, use expo-linking:
App.tsx
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
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
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:
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 in app.json:
app.json
{
  "expo": {
    "plugins": [
      [
        "@shortkit/expo",
        {
          "ios": {
            "backgroundModes": ["audio"]
          },
          "android": {
            "enableChromecast": true
          }
        }
      ]
    ]
  }
}

Debugging

Enable debug mode during development:
shortkit.initialize({
  apiKey: 'pk_live_your_publishable_key',
  config: { /* ... */ },
  environment: __DEV__ ? 'development' : 'production'
});

// View debug logs
shortkit.setLogLevel('verbose');
Use React Native Debugger to inspect SDK calls in the network tab.

Next steps