JSPM

  • Created
  • Published
  • Downloads 60
  • Score
    100M100P100Q60459F
  • License MIT

A React component library for creating Instagram/TikTok-style vertical video swiper

Package Exports

  • react-riyils
  • react-riyils/dist/index.css

Readme

React Riyils

Instagram/TikTok-style vertical video swiper for React.

npm version License: MIT

Demo

Demo 1 Demo 2

Features

  • Vertical video swiper with Instagram Reels/TikTok-style navigation
  • Full-screen viewer with smooth transitions
  • Mobile and desktop responsive
  • Smart video preloading
  • Custom hooks for video control
  • TypeScript support
  • Customizable colors, sizes, and translations

Installation

npm install react-riyils

Usage

import React, { useState } from 'react';
import { ReactRiyils, RiyilsViewer } from 'react-riyils';
import 'react-riyils/dist/index.css';

function App() {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [showViewer, setShowViewer] = useState(false);

  const videos = [
    { id: '1', videoUrl: 'https://example.com/video1.mp4' },
    { id: '2', videoUrl: 'https://example.com/video2.mp4' },
    { id: '3', videoUrl: 'https://example.com/video3.mp4' },
  ];

  return (
    <>
      <ReactRiyils
        videos={videos}
        currentIndex={currentIndex}
        onVideoClick={(index) => setShowViewer(true)}
        onVideoChange={setCurrentIndex}
      />

      {showViewer && (
        <RiyilsViewer
          videos={videos}
          initialIndex={currentIndex}
          onClose={() => setShowViewer(false)}
          onVideoChange={setCurrentIndex}
        />
      )}
    </>
  );
}

API

ReactRiyils Props

Prop Type Default Description
videos Video[] required Array of video objects
currentIndex number required Current active video index
onVideoClick (index: number) => void required Click handler
onVideoChange (index: number) => void required Change handler
containerHeightMobile number 380 Mobile height (px)
containerHeightDesktop number 500 Desktop height (px)
progressBarColor string '#3B82F6' Progress bar color
videoDurationLimit number 10 Auto-next after seconds
autoPlay boolean true Auto-play videos

RiyilsViewer Props

Prop Type Default Description
videos Video[] required Array of video objects
initialIndex number 0 Starting video index
onClose () => void - Close handler
onVideoChange (index: number) => void - Change handler
progressBarColor string '#FF0000' Progress bar color

Video Type

interface Video {
  id: string;
  videoUrl: string;
  thumbnailUrl?: string;
  duration?: number;
}

Lazy Loading / Infinite Scroll

import React, { useState, useEffect } from 'react';
import { ReactRiyils, RiyilsViewer } from 'react-riyils';

function App() {
  const [videos, setVideos] = useState([
    { id: '1', videoUrl: 'https://example.com/video1.mp4' },
    { id: '2', videoUrl: 'https://example.com/video2.mp4' },
  ]);
  const [currentIndex, setCurrentIndex] = useState(0);
  const [showViewer, setShowViewer] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [page, setPage] = useState(1);

  const WINDOW_SIZE = 20; // Keep max 20 videos in memory
  const LOAD_THRESHOLD = 3; // Load more when 3 videos away from end

  const loadMoreVideos = async () => {
    if (isLoading || !hasMore) return;
    
    setIsLoading(true);
    try {
      // Replace with your actual API call
      const response = await fetch(`/api/videos?page=${page}&limit=10`);
      const newVideos = await response.json();
      
      if (newVideos.length === 0) {
        setHasMore(false);
      } else {
        setVideos(prev => {
          const updated = [...prev, ...newVideos];
          // Memory management: remove old videos if exceeds window size
          if (updated.length > WINDOW_SIZE) {
            const removeCount = updated.length - WINDOW_SIZE;
            return updated.slice(removeCount);
          }
          return updated;
        });
        setPage(prev => prev + 1);
      }
    } catch (error) {
      console.error('Failed to load videos:', error);
    } finally {
      setIsLoading(false);
    }
  };

  useEffect(() => {
    // Load more videos when approaching the end
    if (currentIndex >= videos.length - LOAD_THRESHOLD && hasMore) {
      loadMoreVideos();
    }
  }, [currentIndex, videos.length, hasMore]);

  return (
    <>
      <ReactRiyils
        videos={videos}
        currentIndex={currentIndex}
        onVideoClick={(index) => setShowViewer(true)}
        onVideoChange={setCurrentIndex}
      />

      {showViewer && (
        <RiyilsViewer
          videos={videos}
          initialIndex={currentIndex}
          onClose={() => setShowViewer(false)}
          onVideoChange={setCurrentIndex}
        />
      )}
    </>
  );
}

Custom Hooks

// Video playback control
import { useVideoControls } from 'react-riyils';
const { isPlaying, progress, play, pause, togglePlay, seek } = useVideoControls(videoRef.current, isActive);

// Swiper navigation
import { useSwiperControl } from 'react-riyils';
const { swiperRef, currentIndex, goToSlide, nextSlide, prevSlide } = useSwiperControl(0);

// Video preloading
import { useVideoPreload } from 'react-riyils';
useVideoPreload(videoRefs, activeIndex, preloadDistance);

Keyboard Controls

  • Escape - Close viewer
  • Space - Play/Pause
  • M - Mute/Unmute
  • ↑/↓ - Navigate videos

License

MIT