Angular Reel Player

Full-screen Instagram/TikTok-style vertical media player for Angular, built on @reelkit/angular-reel-player.

View live demo →

Features

Vertical Swipe
Touch, drag, keyboard, wheel
Video Autoplay
Plays when visible
Sound Toggle
iOS continuity
Multi-Media
Horizontal nested carousels
Position Memory
Resumes where left off
Frame Capture
Poster-to-video crossfade
Virtualized
Only 3 slides in DOM
Aspect Ratio
9:16 desktop, full mobile
Desktop Nav
Arrow buttons
Generic Types
Custom content data models
Customizable
Template slots for everything
Error Handling
Broken media detection with LRU cache

Installation

bash
Icons
The default controls use lucide-angular for icons (close, sound, navigation arrows). If you prefer a different icon library, use the rkPlayerControls and rkPlayerNavigation template slots to provide your own.

Basic Usage

Import the stylesheet and the standalone RkReelPlayerOverlayComponent into your component's imports array.

reel-feed.component.ts

Template Slots

Six template slot directives let you customize every aspect of the player's UI. Each receives a strongly-typed context object. Only provide the slots you want to override — the defaults are used for the rest.

DirectiveContext TypeDescription
[rkPlayerControls]PlayerControlsContext<T>Custom global controls bar (close, sound toggle, etc.)
[rkPlayerError]{ $implicit: activeIndex, item, innerActiveIndex }Custom error indicator template slot
[rkPlayerLoading]{ $implicit: activeIndex, item, innerActiveIndex }Custom loading indicator template slot
[rkPlayerNavigation]PlayerNavigationContextCustom prev/next navigation arrows
[rkPlayerNestedNavigation]PlayerNestedNavigationContextCustom navigation arrows for the inner horizontal slider
[rkPlayerNestedSlide]PlayerNestedSlideContextCustom content for each slide inside the inner horizontal slider
[rkPlayerSlide]PlayerSlideContext<T>Fully custom slide content replacing the default media slide
[rkPlayerSlideOverlay]PlayerSlideOverlayContext<T>Per-slide overlay (author info, likes, description, etc.)
[rkPlayerTimeline]PlayerTimelineContext<T>Custom playback timeline bar. Rendered only when the gate (timeline mode + min duration) would render the default bar (same auto/always/never logic).
typescript

Custom Timeline

The rkPlayerTimeline template slot is invoked only when the overlay's gating rules would render the default bar (same timeline mode + timelineMinDurationSeconds), so you don't re-implement it. Reuse the .rk-reel-timeline class on your root to inherit flush-bottom positioning, safe-area padding, and touch-device clearance. Call state.bindInteractions(el) on your scrub track to wire pointer + keyboard scrubbing.

Nested Slider (Multi-Media Items)

When a ContentItem contains multiple media entries, the player renders them in a horizontal nested slider (Instagram carousel style). Use the rkPlayerNestedSlide slot to customize the inner slide content.

typescript

Content Loading & Error Handling

The player tracks per-slide loading and error states. A wave loader shows while content loads; an error icon shows for broken media. Errored URLs are cached so revisiting shows the error instantly without retrying.

Lifecycle Callbacks

When using the rkPlayerSlide template slot, use the context callbacks to control the loading indicator:

CallbackWhen to call
onReadyImage loaded or video started playing. Clears loading and error states.
onWaitingVideo is buffering mid-playback. Shows the loading indicator.
onErrorContent failed to load. Shows error overlay and caches the URL as broken.
html

Custom Loading & Error UI

Replace the default wave loader and error icon with custom templates:

html

Timeline

The overlay renders a built-in playback timeline bar over the active video. Gate it via the timeline input: 'auto' (default) renders whenever the active media is a video longer than timelineMinDurationSeconds (default 30), 'always' whenever a video is active, 'never' to disable. For a fully custom scrub bar, use the rkPlayerTimeline template directive; its context exposes a timelineState backed by the underlying TimelineController.

html

Theme via the --rk-reel-timeline-* CSS custom properties. For direct control in custom consumer components, inject TimelineStateService.

RkTimelineBarComponent

Default playback scrub bar component. Consumes TimelineStateService (provided by RkReelPlayerOverlayComponent) and renders the track, buffered ranges, progress fill, and scrub pill. Selector: rk-timeline-bar. Inputs: class?: string, style?: Record<string, string>. Use inside an rkPlayerTimeline template to wrap or augment the default bar; use standalone only inside a consumer that provides the service.

typescript

SoundStateService

Provided at the RkReelPlayerOverlayComponent level. Injected by the default sound button and exposed in the controls template slot context. Can be injected in custom controls that are children of the overlay for direct access.

typescript
MemberTypeDescription
muted()Signal<boolean>Whether the player is currently muted
disabled()Signal<boolean>True when the active slide has no video or is transitioning
toggle()() => voidToggles the muted state

Custom Data Types

Extend BaseContentItem to use your own domain model. The component is generic: RkReelPlayerOverlayComponent<T extends BaseContentItem>.

typescript

RkReelPlayerOverlayComponent Inputs

InputTypeDefaultDescription
ariaLabelstring'Video player'Accessible label for the dialog region
aspectRationumber | undefinedundefinedWidth/height ratio for desktop container. Defaults to 9/16. On mobile the player uses full viewport.
contentT[] (extends BaseContentItem)requiredArray of content items to display in the player
enableNavKeysbooleantrueEnable keyboard arrow key navigation
enableWheelbooleantrueEnable mouse wheel navigation
initialIndexnumber0Zero-based index of the initially visible item
isOpenbooleanrequiredControls overlay visibility; when false the overlay is removed from the DOM
loopbooleanfalseEnable infinite loop between slides
swipeDistanceFactornumber0.12Minimum swipe distance fraction to trigger slide change
timeline'auto' | 'always' | 'never''auto'Gating strategy for the built-in playback timeline bar. 'auto' renders only for videos longer than timelineMinDurationSeconds; 'always' renders whenever the active slide has a video; 'never' disables the built-in bar (use rkPlayerTimeline template slot for a fully custom replacement).
timelineMinDurationSecondsnumber30Minimum video duration (seconds) for timeline='auto' to render the built-in bar. Short looping clips below this threshold are suppressed.
transitionDurationnumber300Slide animation duration in ms
wheelDebounceMsnumber200Debounce duration for wheel events in ms

RkReelPlayerOverlayComponent Outputs

OutputTypeDescription
apiReadyEventEmitter<ReelApi>Emitted once the slider is ready, exposing the imperative API
closedEventEmitter<void>Emitted when the player is closed
slideChangeEventEmitter<number>Emitted when the active slide index changes

MediaItem Interface

FieldTypeDescription
idstringUnique identifier for the media item
type'image' | 'video'Media type
srcstringURL of the media asset
posterstring?Poster thumbnail URL for video items
aspectRationumberwidth/height ratio. Values < 1 indicate vertical (cover), > 1 horizontal (contain)

Template Slot Context Types

TypeFields
PlayerControlsContext<T>{ $implicit: onClose, activeIndex, content: T[], soundState: PlayerSoundState }
PlayerNavigationContext{ $implicit: onPrev, onNext, activeIndex, count }
PlayerNestedNavigationContext{ $implicit: onPrev, onNext, activeIndex, count }
PlayerNestedSlideContext{ $implicit: MediaItem, index, size, isActive, isInnerActive, slideKey }
PlayerSlideContext<T>{ $implicit: T, index, size: [number,number], isActive, slideKey, onReady, onWaiting, onError }
PlayerSlideOverlayContext<T>{ $implicit: T, index, isActive }
PlayerTimelineContext<T>{ $implicit: T, activeIndex, timelineState: PlayerTimelineState }
PlayerTimelineState{ duration(), currentTime(), progress(), bufferedRanges(), isScrubbing(), seek(t), bindInteractions(el) }

CSS Classes

All CSS classes are plain (not scoped), so they can be targeted with higher-specificity selectors in a stylesheet loaded after @reelkit/angular-reel-player/styles.css. For color, size, and z-index changes, prefer the CSS custom properties documented in the Theming section below — they're designed for exactly that.

ClassComponentDescription
.rk-reel-overlayOverlayFixed full-screen backdrop (background, z-index)
.rk-reel-containerOverlayPlayer container (position, overflow)
.rk-reel-loaderOverlayWave loading animation overlay
.rk-reel-media-errorOverlayError state overlay (centered icon + text)
.rk-reel-media-error-textOverlayError message text
.rk-reel-buttonControlsShared circular icon button (close, sound, nav arrows)
.rk-reel-close-btnControlsClose button
.rk-reel-sound-btnControlsSound toggle button
.rk-reel-nav-arrowsNavigationDesktop-only arrow container (hidden below 768px)
.rk-reel-nav-btnNavigationIndividual prev/next nav arrow
.rk-reel-slide-wrapperSlideWrapper around media + overlay
.rk-reel-slide-overlaySlideOverlayGradient overlay container
.rk-reel-slide-overlay-authorSlideOverlayAuthor row (avatar + name)
.rk-reel-slide-overlay-avatarSlideOverlayAuthor avatar image
.rk-reel-slide-overlay-nameSlideOverlayAuthor name text
.rk-reel-slide-overlay-descriptionSlideOverlayDescription text
.rk-reel-slide-overlay-likesSlideOverlayLikes row (heart + count)
.rk-reel-video-containerVideoSlideVideo wrapper (background, overflow)
.rk-reel-video-elementVideoSlideThe <video> element
.rk-reel-video-posterVideoSlidePoster image (fades out on play)
.rk-reel-video-loaderVideoSlideWave loading animation
.rk-reel-video-poster.rk-visibleVideoSlideState modifier applied to the poster while the video is paused/loading
.rk-reel-nested-indicatorNestedSliderDot pagination under multi-media slides (position varies desktop vs. touch)
.rk-reel-nested-navNestedSliderHorizontal carousel arrows (hidden below 768px)
.rk-reel-nested-nav-nextNestedSliderNested next arrow position
.rk-reel-nested-nav-prevNestedSliderNested prev arrow position
.rk-reel-nested-slider-innerNestedSliderNested horizontal slider root
.rk-reel-timelineTimelineBarScrub-bar wrapper. Reuse on custom `rkPlayerTimeline` template roots to inherit flush-bottom positioning, safe-area padding, and touch-device slide-overlay clearance.
.rk-reel-timeline-trackTimelineBarTrack (unplayed region)
.rk-reel-timeline-bufferedTimelineBarBuffered segments layer
.rk-reel-timeline-fillTimelineBarPlayed-progress fill
.rk-reel-timeline-cursorTimelineBarScrub-handle pill (floats above the track)

Theming

Every color, size, z-index, and transition lives in a CSS custom property. Override one or many at :root (or any ancestor of the overlay) to retheme without touching component source. The tokens match the React and Vue packages, so overrides port between bindings.

TokenDefaultControls
--rk-reel-overlay-bg#000Full-screen backdrop color
--rk-reel-overlay-z1000Overlay z-index
--rk-reel-button-bgrgba(0, 0, 0, 0.5)Default circular button background
--rk-reel-button-bg-hoverrgba(255, 255, 255, 0.1)Nav arrow background (and base hover state)
--rk-reel-button-bg-hover-strongrgba(255, 255, 255, 0.2)Nav arrow hover background
--rk-reel-button-fg#fffButton icon color
--rk-reel-button-size44pxButton width / height
--rk-reel-button-radius50%Button border-radius
--rk-reel-ui-z10Close / sound / nav z-index
--rk-reel-edge-padding16pxEdge inset for close / sound / nav arrows
--rk-reel-nav-gap8pxSpacing between stacked nav arrows
--rk-reel-transition0.2sHover transition duration
--rk-reel-loader-colorrgba(255, 255, 255, 0.12)Wave loader gradient color
--rk-reel-loader-duration1.8sWave loader animation duration
--rk-reel-error-fgrgba(255, 255, 255, 0.4)Error icon and text color
--rk-reel-slide-overlay-bglinear-gradient(transparent, rgba(0, 0, 0, 0.7))Caption scrim gradient
--rk-reel-slide-overlay-padding48px 16px 16pxCaption inner padding
--rk-reel-slide-overlay-name-color#fffAuthor name color
--rk-reel-video-bg#000Letterbox background behind <video>
--rk-reel-video-loader-colorrgba(255, 255, 255, 0.15)Video buffering shimmer color
--rk-reel-nested-button-bgrgba(0, 0, 0, 0.5)Nested arrow background
--rk-reel-nested-button-size36pxNested arrow size
--rk-reel-nested-edge-padding12pxNested arrow edge inset
--rk-reel-timeline-trackrgba(255, 255, 255, 0.22)Track background (unplayed region)
--rk-reel-timeline-bufferedrgba(255, 255, 255, 0.4)Buffered segments color
--rk-reel-timeline-fill#fffPlayed-progress fill color
--rk-reel-timeline-cursor#fffScrub-handle pill color
--rk-reel-timeline-height3pxTrack height at rest
--rk-reel-timeline-height-active6pxTrack height on hover / focus / scrub
--rk-reel-timeline-cursor-width10pxScrub-pill width at rest
--rk-reel-timeline-cursor-width-active14pxScrub-pill width while scrubbing
--rk-reel-timeline-cursor-height24pxScrub-pill height at rest
--rk-reel-timeline-cursor-height-active32pxScrub-pill height while scrubbing
--rk-reel-timeline-transition0.15s ease-outTrack + pill grow/shrink animation

Drop the snippet below into a stylesheet loaded after @reelkit/angular-reel-player/styles.css.

css

Accessibility

The overlay root is a modal dialog (role="dialog", aria-modal="true"). Set the ariaLabel input to change the screen-reader announcement; it defaults to "Video player". Each slide carries role="group", aria-roledescription="slide", and aria-label="Slide N of M".

The overlay captures focus on open and returns it to the trigger on close. Tab and Shift+Tab cycle through focusable elements inside; focus that escapes (click outside, programmatic focus) gets pulled back. Implemented with captureFocusForReturn and createFocusTrap from @reelkit/core.

Keyboard Shortcuts

KeyAction
ArrowUpPrevious slide
ArrowDownNext slide
ArrowLeftPrevious media (in nested slider)
ArrowRightNext media (in nested slider)
EscapeClose player