FeedConfig() with no arguments produces a working feed.
ShortKit initializer
- Swift
- Kotlin
- Flutter
- React Native
- Web
public final class ShortKit {
public init(
apiKey: String,
config: FeedConfig,
userId: String? = nil,
adProvider: ShortKitAdProvider? = nil,
clientAppName: String? = nil,
clientAppVersion: String? = nil,
customDimensions: [String: String]? = nil,
feedItems: [FeedInput]? = nil
)
}
class ShortKit(
apiKey: String,
config: FeedConfig,
userId: String? = null,
adProvider: ShortKitAdProvider? = null,
clientAppName: String? = null,
clientAppVersion: String? = null,
customDimensions: Map<String, String>? = null,
feedItems: List<FeedInput>? = null
)
ShortKitController({
required String apiKey,
String? userId,
String? clientAppName,
String? clientAppVersion,
Map<String, String>? customDimensions,
})
// Or use the provider shorthand:
ShortKitProvider.apiKey(
apiKey: 'pk_live_...',
userId: 'user-123', // optional
clientAppName: 'MyApp', // optional
customDimensions: {'custom_1': 'sports'}, // optional
child: const ShortKitFeed(),
)
Flutter does not pass
FeedConfig at initialization. Configuration is passed directly to ShortKitFeed(config: ...) instead.import { ShortKitProvider } from '@shortkitsdk/react-native';
interface ShortKitProviderProps {
apiKey: string;
config: FeedConfig;
userId?: string;
clientAppName?: string;
clientAppVersion?: string;
customDimensions?: Record<string, string>;
children: React.ReactNode;
}
const sk = new ShortKit({
apiKey: 'pk_live_your_publishable_key',
userId: 'user-123', // optional
clientAppName: 'MyApp', // optional
customDimensions: { // optional
custom_1: 'sports',
},
});
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey | String | required | Your publishable API key. Starts with pk_live_ or pk_test_. |
config | FeedConfig | required | Feed layout and behavior configuration. See FeedConfig below. |
userId | String? | nil | Known user ID. When provided, the SDK starts in identified mode immediately, skipping the anonymous-first flow. |
adProvider | ShortKitAdProvider? | nil | Ad provider for monetization. Pass nil to disable ads. |
clientAppName | String? | nil | App name included in analytics metadata. |
clientAppVersion | String? | nil | App version included in analytics metadata. |
customDimensions | [String: String]? | nil | Custom key-value pairs for analytics segmentation. Keys must be "custom_1" through "custom_5". |
feedItems | [FeedInput]? | nil | Content items to preload when using custom feed mode (feedSource: .custom). See Custom feed input for the shape. |
FeedConfig
- Swift
- Kotlin
- Flutter
- React Native
- Web
public struct FeedConfig: Codable {
public var feedHeight: FeedHeight
public var scrollAxis: ScrollAxis
public var videoOverlay: VideoOverlayMode
public var carouselOverlay: CarouselOverlayMode
public var videoCarouselOverlay: VideoCarouselOverlayMode
public var surveyOverlay: SurveyOverlayMode
public var adOverlay: AdOverlayMode
public var muteOnStart: Bool
public var autoplay: Bool
public var feedSource: FeedSource
public var filter: FeedFilter?
public init(
feedHeight: FeedHeight = .fullscreen,
scrollAxis: ScrollAxis = .vertical,
videoOverlay: VideoOverlayMode = .none,
carouselOverlay: CarouselOverlayMode = .none,
videoCarouselOverlay: VideoCarouselOverlayMode = .none,
surveyOverlay: SurveyOverlayMode = .none,
adOverlay: AdOverlayMode = .none,
muteOnStart: Bool = true,
autoplay: Bool = true,
feedSource: FeedSource = .algorithmic,
filter: FeedFilter? = nil
)
}
data class FeedConfig(
val feedHeight: FeedHeight = FeedHeight.Fullscreen,
val scrollAxis: ScrollAxis = ScrollAxis.Vertical,
val videoOverlay: VideoOverlayMode = VideoOverlayMode.None,
val carouselOverlay: CarouselOverlayMode = CarouselOverlayMode.None,
val videoCarouselOverlay: VideoCarouselOverlayMode = VideoCarouselOverlayMode.None,
val surveyOverlay: SurveyOverlayMode = SurveyOverlayMode.None,
val adOverlay: AdOverlayMode = AdOverlayMode.None,
val muteOnStart: Boolean = true,
val autoplay: Boolean = true,
val feedSource: FeedSource = FeedSource.Algorithmic,
val filter: FeedFilter? = null
)
FeedConfig({
FeedHeight? feedHeight, // FeedHeightFullscreen() or FeedHeightPercentage(0.85)
ScrollAxis? scrollAxis, // ScrollAxis.vertical or ScrollAxis.horizontal
OverlayConfig? overlay, // OverlayConfigNone() or OverlayConfigCustom('name')
SurveyMode? surveyMode, // SurveyModeNone() or SurveyModeTemplate('default')
bool? muteOnStart, // default: true
bool? autoplay, // default: true
FeedSource? feedSource, // FeedSource.algorithmic or FeedSource.custom
FeedFilter? filter,
})
Flutter uses
overlay (not videoOverlay) and sealed classes for enum-like types (FeedHeight, OverlayConfig, SurveyMode).interface FeedConfig {
feedHeight?: FeedHeight;
scrollAxis?: ScrollAxis;
overlay?: OverlayConfig;
carouselOverlay?: CarouselOverlayConfig;
videoCarouselOverlay?: VideoCarouselOverlayConfig;
surveyMode?: SurveyMode;
muteOnStart?: boolean;
autoplay?: boolean;
feedSource?: 'algorithmic' | 'custom';
filter?: FeedFilter;
}
type FeedHeight = { type: 'fullscreen' } | { type: 'percentage'; value: number };
type ScrollAxis = 'vertical' | 'horizontal';
type OverlayConfig = 'none' | { type: 'custom'; component: React.ComponentType };
type CarouselOverlayConfig = 'none' | { type: 'custom'; name: string; component: React.ComponentType };
type VideoCarouselOverlayConfig = 'none' | { type: 'custom'; name: string; component: React.ComponentType };
type SurveyMode = 'none' | { type: 'template'; name: 'default' };
React Native uses different property names than the native SDKs:
overlay (not videoOverlay), surveyMode (not surveyOverlay), and does not support adOverlay. See Overlays for details.const feed = sk.createFeed(document.getElementById('feed'), {
config: {
feedHeight: 'fullscreen', // or { type: 'percentage', value: 0.85 }
muteOnStart: true,
autoplay: true,
feedSource: 'algorithmic', // or 'custom'
filter: { section: 'news', tags: ['breaking'] },
overlay: (container, { item, player, feed }) => {
// render callback for custom overlay
},
},
});
| Property | Type | Default | Description |
|---|---|---|---|
feedHeight | FeedHeight | .fullscreen | .fullscreen fills the entire screen. .percentage(CGFloat) sets a fraction of screen height (e.g. 0.85 for 85%). |
scrollAxis | ScrollAxis | .vertical | .vertical for a standard up/down swipe feed. .horizontal for a left/right swipe feed. Each cell fills the feed container in both modes. |
videoOverlay | VideoOverlayMode | .none | .none disables overlays. .custom { ... } provides a developer-built overlay managed by the SDK. See Overlays. |
carouselOverlay | CarouselOverlayMode | .none | .none disables carousel UI. .custom { ... } provides a developer-built carousel overlay. See Overlays. |
videoCarouselOverlay | VideoCarouselOverlayMode | .none | .none shows only the SDK’s native video pager with built-in page indicators. .custom { ... } provides a developer-built overlay rendered on top of the native video pager. See Overlays. |
surveyOverlay | SurveyOverlayMode | .none | .none disables survey UI. .custom { ... } provides a developer-built survey overlay. See Overlays. |
adOverlay | AdOverlayMode | .none | .none disables native ad overlays. .custom { ... } provides a developer-built ad overlay. See Overlays. Only applies to native ads; system-defined ads (IMA/VAST) are unaffected. |
muteOnStart | Bool | true | Whether video starts muted. Once the user unmutes, the unmuted state persists for the session. |
autoplay | Bool | true | Whether video starts playing automatically when the feed loads or when the user swipes to a new item. |
feedSource | FeedSource | .algorithmic | .algorithmic fetches content from ShortKit’s recommendation engine. .custom uses client-provided items (pass via feedItems on ShortKit.init or call setFeedItems() / appendFeedItems()). |
filter | FeedFilter? | nil | Server-side content filter. When set, the SDK fetches only content matching the filter criteria. See FeedFilter below. |
- Swift
- Kotlin
- Flutter
- React Native
- Web
// 70% of screen height, leaving room for a tab bar
let config = FeedConfig(feedHeight: .percentage(0.7))
// 70% of screen height, leaving room for a tab bar
val config = FeedConfig(feedHeight = FeedHeight.Percentage(0.7f))
// 70% of screen height, leaving room for a tab bar
ShortKitFeed(
config: FeedConfig(feedHeight: FeedHeightPercentage(0.7)),
)
// 70% of screen height, leaving room for a tab bar
<ShortKitProvider
apiKey="pk_live_your_publishable_key"
config={{ feedHeight: { type: 'percentage', value: 0.7 } }}
>
<ShortKitFeed style={{ flex: 1 }} />
</ShortKitProvider>
// 70% of screen height, leaving room for a tab bar
const feed = sk.createFeed(document.getElementById('feed'), {
config: {
feedHeight: { type: 'percentage', value: 0.7 },
},
});
Horizontal feed
SetscrollAxis to .horizontal for a left/right swipe feed instead of the default vertical (up/down) feed. Each cell still fills the full feed container — only the swipe direction changes.
- Swift
- Kotlin
- Flutter
- React Native
- Web
let config = FeedConfig(scrollAxis: .horizontal)
val config = FeedConfig(scrollAxis = ScrollAxis.Horizontal)
ShortKitFeed(
config: FeedConfig(scrollAxis: ScrollAxis.horizontal),
)
<ShortKitFeed config={{ scrollAxis: 'horizontal' }} style={{ flex: 1 }} />
const feed = sk.createFeed(document.getElementById('feed'), {
config: { scrollAxis: 'horizontal' },
});
FeedFilter
FeedFilter controls which content the SDK fetches from the server. When set on FeedConfig, all feed requests (initial load, pagination, and pull-to-refresh) automatically apply the filter. When nil, the full unfiltered feed is returned.
Different filter fields are combined with AND logic — content must match all specified fields. Tags use OR logic within the tags array — content matching any of the provided tags is included.
- Swift
- Kotlin
- Flutter
- React Native
- Web
public struct FeedFilter: Codable, Equatable, Sendable {
public var tags: [String]?
public var section: String?
public var author: String?
public var contentType: String?
public var metadata: [String: String]?
public init(
tags: [String]? = nil,
section: String? = nil,
author: String? = nil,
contentType: String? = nil,
metadata: [String: String]? = nil
)
}
data class FeedFilter(
val tags: List<String>? = null,
val section: String? = null,
val author: String? = null,
val contentType: String? = null,
val metadata: Map<String, String>? = null
)
FeedFilter({
List<String>? tags,
String? section,
String? author,
String? contentType,
Map<String, String>? metadata,
})
interface FeedFilter {
tags?: string[];
section?: string;
author?: string;
contentType?: string;
metadata?: Record<string, string>;
}
const filter = {
tags: ['sports', 'news'], // OR semantics
section: 'front-page', // exact match
author: 'Jane Doe', // exact match
contentType: 'video', // 'video', 'carousel', or 'video_carousel'
metadata: { country: 'Japan' }, // all pairs must match (AND)
};
| Property | Type | Description |
|---|---|---|
tags | [String]? | Filter by content tags. OR semantics — content matching any of the provided tags is included. |
section | String? | Exact match on content section. |
author | String? | Exact match on content author. |
contentType | String? | Filter by content type: "video", "carousel", or "video_carousel". |
metadata | [String: String]? | Filter by custom metadata key-value pairs. All pairs must match (AND). |
- Swift
- Kotlin
- Flutter
- React Native
- Web
// Show only sports and news content
let config = FeedConfig(
filter: FeedFilter(tags: ["sports", "news"])
)
// Show only a specific author's content tagged "politics"
let config = FeedConfig(
filter: FeedFilter(tags: ["politics"], author: "Jane Doe")
)
// Filter by custom metadata
let config = FeedConfig(
filter: FeedFilter(metadata: ["country": "Japan", "vibe": "cultural"])
)
// Filter by detected language
let config = FeedConfig(
filter: FeedFilter(metadata: ["language": "es"])
)
// Show only sports and news content
val config = FeedConfig(
filter = FeedFilter(tags = listOf("sports", "news"))
)
// Filter by custom metadata
val config = FeedConfig(
filter = FeedFilter(metadata = mapOf("country" to "Japan"))
)
// Filter by detected language
val config = FeedConfig(
filter = FeedFilter(metadata = mapOf("language" to "es"))
)
// Show only sports and news content
final config = FeedConfig(
filter: FeedFilter(tags: ['sports', 'news']),
);
// Show only a specific author's content tagged "politics"
final config = FeedConfig(
filter: FeedFilter(tags: ['politics'], author: 'Jane Doe'),
);
// Filter by custom metadata
final config = FeedConfig(
filter: FeedFilter(metadata: {'country': 'Japan', 'vibe': 'cultural'}),
);
// Filter by detected language
final config = FeedConfig(
filter: FeedFilter(metadata: {'language': 'es'}),
);
// Show only sports and news content
<ShortKitProvider
apiKey="pk_live_..."
config={{ filter: { tags: ['sports', 'news'] } }}
>
<ShortKitFeed style={{ flex: 1 }} />
</ShortKitProvider>
// Filter by detected language
<ShortKitProvider
apiKey="pk_live_..."
config={{ filter: { metadata: { language: 'es' } } }}
>
<ShortKitFeed style={{ flex: 1 }} />
</ShortKitProvider>
// Show only sports and news content
sk.createFeed(el, {
config: { filter: { tags: ['sports', 'news'] } },
});
// Show only a specific author's content tagged "politics"
sk.createFeed(el, {
config: { filter: { tags: ['politics'], author: 'Jane Doe' } },
});
// Filter by custom metadata
sk.createFeed(el, {
config: { filter: { metadata: { country: 'Japan', vibe: 'cultural' } } },
});
// Filter by detected language
sk.createFeed(el, {
config: { filter: { metadata: { language: 'es' } } },
});
Filtering by language
ShortKit automatically detects the spoken language in every video and stores it ascustomMetadata.language using a BCP 47 code. Use this with metadata to serve a language-specific feed without any extra work on upload.
- Swift
- Kotlin
- Flutter
- React Native
- Web
// Spanish-language feed
let config = FeedConfig(
filter: FeedFilter(metadata: ["language": "es"])
)
// Spanish-language feed
val config = FeedConfig(
filter = FeedFilter(metadata = mapOf("language" to "es"))
)
// Spanish-language feed
final config = FeedConfig(
filter: FeedFilter(metadata: {'language': 'es'}),
);
// Spanish-language feed
<ShortKitProvider
apiKey="pk_live_..."
config={{ filter: { metadata: { language: 'es' } } }}
>
<ShortKitFeed style={{ flex: 1 }} />
</ShortKitProvider>
// Spanish-language feed
sk.createFeed(el, {
config: { filter: { metadata: { language: 'es' } } },
});
"en" English, "es" Spanish, "fr" French, "de" German, "it" Italian, "pt" Portuguese, "ja" Japanese, "ko" Korean, "zh" Chinese, "ar" Arabic, "hi" Hindi. "und" indicates no detectable speech (music or ambient audio). Language is set asynchronously — it appears on the content object within a few minutes of the video reaching ready status.
Complete example
A full initialization with all configuration options set explicitly.- Swift (SwiftUI)
- Swift (UIKit)
- Kotlin
- Flutter
- React Native
- Web
App.swift
import SwiftUI
import ShortKitSDK
@main
struct MyApp: App {
let shortKit: ShortKit
init() {
let config = FeedConfig(
feedHeight: .fullscreen,
videoOverlay: .none,
muteOnStart: true
)
shortKit = ShortKit(
apiKey: "pk_live_your_publishable_key",
config: config,
clientAppName: "MyNewsApp",
clientAppVersion: "2.4.1",
customDimensions: ["custom_1": "sports"]
)
}
var body: some Scene {
WindowGroup {
ShortKitFeedView(shortKit: shortKit)
.ignoresSafeArea()
}
}
}
ViewController.swift
import UIKit
import ShortKitSDK
class FeedViewController: UIViewController {
private var shortKit: ShortKit!
override func viewDidLoad() {
super.viewDidLoad()
let config = FeedConfig(
feedHeight: .fullscreen,
videoOverlay: .none,
muteOnStart: true
)
shortKit = ShortKit(
apiKey: "pk_live_your_publishable_key",
config: config,
clientAppName: "MyNewsApp",
clientAppVersion: "2.4.1",
customDimensions: ["custom_1": "sports"]
)
let feedVC = ShortKitFeedViewController(shortKit: shortKit)
addChild(feedVC)
feedVC.view.frame = view.bounds
feedVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(feedVC.view)
feedVC.didMove(toParent: self)
}
}
MainActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dev.shortkit.ShortKit
import dev.shortkit.FeedConfig
import dev.shortkit.FeedHeight
import dev.shortkit.VideoOverlayMode
import dev.shortkit.ShortKitFeedView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val config = FeedConfig(
feedHeight = FeedHeight.Fullscreen,
videoOverlay = VideoOverlayMode.None,
muteOnStart = true
)
val shortKit = ShortKit(
apiKey = "pk_live_your_publishable_key",
config = config,
clientAppName = "MyNewsApp",
clientAppVersion = "2.4.1",
customDimensions = mapOf("custom_1" to "sports")
)
val feedView = ShortKitFeedView(this, shortKit)
setContentView(feedView)
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:shortkit_flutter/shortkit_flutter.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final config = FeedConfig(
feedHeight: FeedHeightFullscreen(),
overlay: OverlayConfigNone(),
muteOnStart: true,
);
return MaterialApp(
home: ShortKitProvider.apiKey(
apiKey: 'pk_live_your_publishable_key',
clientAppName: 'MyNewsApp',
clientAppVersion: '2.4.1',
customDimensions: {'custom_1': 'sports'},
child: Scaffold(
body: ShortKitFeed(config: config),
),
),
);
}
}
App.tsx
import React from 'react';
import { ShortKitProvider, ShortKitFeed } from '@shortkitsdk/react-native';
import type { FeedConfig } from '@shortkitsdk/react-native';
const config: FeedConfig = {
feedHeight: { type: 'fullscreen' },
overlay: 'none',
muteOnStart: true,
};
export default function App() {
return (
<ShortKitProvider
apiKey="pk_live_your_publishable_key"
config={config}
clientAppName="MyNewsApp"
clientAppVersion="2.4.1"
customDimensions={{ custom_1: 'sports' }}
>
<ShortKitFeed style={{ flex: 1 }} />
</ShortKitProvider>
);
}
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://sdk.shortkit.dev/web/0.3.0/shortkit.min.js"></script>
</head>
<body>
<div id="feed" style="width:100%;height:100vh;"></div>
<script>
const { ShortKit } = window.ShortKit;
const sk = new ShortKit({
apiKey: 'pk_live_your_publishable_key',
clientAppName: 'MyNewsApp',
customDimensions: { custom_1: 'sports' },
});
sk.createFeed(document.getElementById('feed'), {
config: {
feedHeight: 'fullscreen',
muteOnStart: true,
autoplay: true,
},
});
</script>
</body>
</html>