@zencastr/ui-components
v0.28.0
Published
A standalone React component library. Currently includes a vertical video feed with HLS stream support.
Maintainers
Keywords
Readme
@zencastr/ui-components
A standalone React component library. Currently includes a vertical video feed with HLS stream support.
Installation
npm install @zencastr/ui-componentsUsage
import { VideoFeed } from '@zencastr/ui-components'
<VideoFeed
items={items}
aspectRatio="16/9"
onFetchMore={async () => fetchNextPage()}
onJoinLive={(item) => router.push(item.sessionUrl)}
onImpressionOpen={(data) => analytics.track('impressionOpen', data)}
onPlayStart={(data) => analytics.track('playStart', data)}
/>CSS is automatically injected when you import a component — no separate CSS import required.
For Gilroy font support, import the font stylesheet:
import '@zencastr/ui-components/dist/gilroy.css'Analytics
Analytics is entirely callback-driven. All tracking events are exposed as optional props on <VideoFeed>. Pass callbacks for the events you want to track — the component never calls any analytics SDK directly.
import { VideoFeed } from '@zencastr/ui-components'
import type { FeedCallbacks } from '@zencastr/ui-components'
// Wire up to your analytics provider (Rudderstack, Segment, Amplitude, etc.)
const analyticsCallbacks: FeedCallbacks = {
onImpressionOpen: (data) => analytics.track('impressionOpen', data),
onImpressionClose: (data) => analytics.track('impressionClose', data),
onPlayStart: (data) => analytics.track('playStart', data),
onPlayPause: (data) => analytics.track('playPause', data),
onPlayResume: (data) => analytics.track('playResume', data),
onSeek: (data) => analytics.track('seek', data),
onEndReached: (data) => analytics.track('endReached', data),
onPlayNext: (data) => analytics.track('playNext', data),
onPlayProgress: (data) => analytics.track('playProgress', data),
onViewabilityTick: (data) => analytics.track('viewabilityTick', data),
onFeedRequest: (data) => analytics.track('feedRequest', data),
onFeedResponse: (data) => analytics.track('feedResponse', data),
onJoinAttempted: (data) => analytics.track('joinAttempted', data),
}
<VideoFeed items={items} {...analyticsCallbacks} />Each callback receives a strongly-typed data payload. See the FeedCallbacks type for full documentation.
Development Commands
# Install dependencies
npm install
# Start dev server (src/App.tsx demo app)
npm run dev
# Build the library (outputs to dist/)
npm run build
# Lint
npm run lintPublishing
# Bump version in package.json first, then:
npm run build
npm publishExports
VideoFeed
The main component. Renders a vertical, swipeable video feed with HLS support.
import { VideoFeed } from '@zencastr/ui-components'| Prop | Type | Default | Description |
|------|------|---------|-------------|
| items | IVideoFeedItem[] | [] | Feed items to display |
| aspectRatio | string | '16/9' | CSS aspect-ratio for the container |
| onFetchMore | () => Promise<IVideoFeedItem[]> | — | Called when feed is near end |
| onJoinLive | (item) => void | — | Called when user clicks Join on a live item |
| rankOffset | number | 0 | Starting rank position offset |
| className | string | — | Optional CSS class on the container |
| style | CSSProperties | — | Optional inline styles |
| onImpressionOpen | (data: ImpressionOpenData) => void | — | Slide becomes active (300ms delay) |
| onImpressionClose | (data: ImpressionCloseData) => void | — | Slide leaves (500ms delay) |
| onPlayStart | (data: PlayStartData) => void | — | Autoplay begins |
| onPlayPause | (data: PlayPauseData) => void | — | User pauses |
| onPlayResume | (data: PlayResumeData) => void | — | User resumes after pause |
| onSeek | (data: SeekData) => void | — | User seeks |
| onEndReached | (data: EndReachedData) => void | — | Video reaches end (clips) |
| onPlayNext | (data: PlayNextData) => void | — | User scrolls to next slide |
| onPlayProgress | (data: PlayProgressData) => void | — | Debounced every 2s while playing |
| onViewabilityTick | (data: ViewabilityTickData) => void | — | Every 1s while visible |
| onFeedRequest | (data: FeedRequestData) => void | — | Before fetch-more request |
| onFeedResponse | (data: FeedResponseData) => void | — | After fetch-more completes |
| onJoinAttempted | (data: JoinAttemptedData) => void | — | User clicks Join on live item |
VideoFeedModel
MobX observable model that powers VideoFeed. Useful for programmatic control (e.g. slideNext()).
import { VideoFeedModel } from '@zencastr/ui-components'
const model = new VideoFeedModel({ callbacks: analyticsCallbacks, rankOffset: 0 })
model.startNewSession(items)
model.slideNext()FeedItemModel
MobX observable model for individual feed items. Manages impressions, tracking callbacks, and media lifecycle.
import { FeedItemModel } from '@zencastr/ui-components'Types
import type {
VideoFeedProps,
IVideoFeedItem,
VideoFeedItemType,
FeedCallbacks,
VideoFeedTrackEvent,
// Event data types:
ImpressionOpenData, ImpressionCloseData,
PlayStartData, PlayPauseData, PlayResumeData,
SeekData, EndReachedData, PlayNextData,
PlayProgressData, ViewabilityTickData,
FeedRequestData, FeedResponseData,
JoinAttemptedData,
} from '@zencastr/ui-components'VIDEO_FEED_TRACK_EVENTS
Constant map of event name strings.
import { VIDEO_FEED_TRACK_EVENTS } from '@zencastr/ui-components'
// VIDEO_FEED_TRACK_EVENTS.IMPRESSION_OPEN, etc.Item Shape
interface IVideoFeedItem {
_id: string
type: 'clip' | 'live'
videoUrl: string // .m3u8 HLS or any HTML5 video URL
videoWidth: number
videoHeight: number
title: string
ownerUsername: string
ownerDisplayName: string
ownerAvatarUrl: string
showThumbnailUrl: string
sessionUrl?: string // when set, a "Join" button is shown
liveStreamStartedAt?: string
}Architecture
Each root-level folder under lib/ is an independently exportable component. The build uses Vite library mode with per-file entry points. CSS is output as a single standalone stylesheet — consumers must import it explicitly via import '@zencastr/ui-components/styles.css'.
lib/
├── index.ts # Public re-exports
├── utils/ # Shared utilities
└── VideoFeed/ # Root component
├── VideoFeed.tsx # Main component (Swiper carousel)
├── VideoFeed.model.ts # MobX model (slides, session, callbacks)
├── VideoFeed.types.ts # TypeScript interfaces & event data types
├── VideoFeedItem/ # Individual feed item
├── VideoPlayer/ # HLS/native video playback
└── PlayerControls/ # Seek bar, play/pause, volume, speedState management uses MobX observable models (.model.ts files). Each visual sub-component is a small observer() to minimise re-renders.
