Building a Custom Video Player in React JS with Custom Controls and Elegant Design

Building a Custom Video Player in React JS with Custom Controls and Elegant Design
2024-10-23Intermediate5 min

Creating a custom video player in React opens up a world of flexibility and design possibilities. You can build something that goes beyond the standard HTML video element, giving your users a sleek, modern interface along with rich functionality like custom play/pause buttons, volume control, speed adjustments, and even quality settings.

In this blog, I'll explain how you can create a React video player with custom controls. I’ll focus on some of the most important functions that bring your player to life. After the explanation, I'll provide the full code so you can dive right into the development process.

Key Features of the Video Player

Before we get into the technical details, here’s what our custom video player will offer:

  • Play/Pause Control: Custom buttons to toggle between playing and pausing the video.
  • Volume Control: Adjustable volume with a slider.
  • Seek Bar: Users can jump to any part of the video by dragging a seek bar.
  • Speed Control: Options to change playback speed (e.g., 0.5x, 1x, 1.5x, 2x).
  • Video Quality Selection: Users can change video quality based on the available resolutions.
  • Elegant Design: A dark mode compatible interface that feels polished and modern.
  • The Heart of the Video Player: Key Functions

    Play/Pause Toggle

    The togglePlay function is the foundation of the video player. It allows the user to start or pause the video with a single button click.

    const togglePlay = () => {
        if (videoRef.current.paused) {
            videoRef.current.play();
            setIsPlaying(true);
        } else {
            videoRef.current.pause();
            setIsPlaying(false);
        }
    };

    Here, we use the videoRef to directly control the video element. The function checks if the video is paused or playing and then changes the state accordingly.

    Handling Volume

    Volume control is another essential feature for any video player. We handle this through the handleVolumeChange function, which updates the volume both in the UI and on the actual video element.

    const handleVolumeChange = (e) => {
        const newVolume = parseFloat(e.target.value);
        setVolume(newVolume);
        videoRef.current.volume = newVolume;
    };

    This function listens for changes in the volume slider and synchronizes it with the video's volume level in real-time.

    Seek Bar for Video Scrubbing

    A seek bar allows users to jump to different parts of the video. This is achieved by using the handleSeek function:

    const handleSeek = (e) => {
        const time = parseFloat(e.target.value);
        setCurrentTime(time);
        videoRef.current.currentTime = time;
    };

    Here, we update the currentTime of the video when the user interacts with the seek bar, making video scrubbing intuitive and smooth.

    Video Speed Control

    Changing playback speed is a neat feature, especially for instructional videos. This is handled by the handleSpeedChange function:

    const handleSpeedChange = (speed) => {
        setCurrentSpeed(speed);
        videoRef.current.playbackRate = speed;
    };

    Users can choose from different playback speeds, and the video adjusts instantly.

    Fullscreen Toggle

    A modern video player wouldn't be complete without fullscreen support. The toggleFullscreen function provides this:

    const toggleFullscreen = () => {
        if (videoRef.current) {
            if (document.fullscreenElement) {
                document.exitFullscreen();
            } else {
                videoRef.current.requestFullscreen();
            }
        }
    };

    This function checks if the video is already in fullscreen mode and toggles it accordingly.

    Elegant Design Considerations

    Design plays a crucial role in how users perceive your video player. In this build, I’ve designed the player to be smooth transitions, transparent backgrounds, and intuitive button placement. Controls like play/pause, volume, and the seek bar are only visible when the user hovers over the video. This keeps the interface clean and non-intrusive while watching videos.

    Building a Custom Video Player in React JS with Custom Controls and Elegant Design
    Building a Custom Video Player in React JS with Custom Controls and Elegant Design
    Building a Custom Video Player in React JS with Custom Controls and Elegant Design

    Full Code

    Here’s the complete code for the custom video player, including all features discussed above:

    import React, { useState, useEffect, useRef } from 'react';
    
    const videos = [
      { id: 1, title: 'Advanced React Hooks Tutorial', channel: 'React Mastery', views: '1.2M views', timestamp: '2 weeks ago', thumbnail: 'https://www.cibaky.com/wp-content/uploads/2015/12/placeholder-2.jpg' },
      { id: 2, title: 'Building Scalable APIs with GraphQL', channel: 'Backend Wizards', views: '890K views', timestamp: '1 month ago', thumbnail: 'https://www.cibaky.com/wp-content/uploads/2015/12/placeholder-2.jpg' },
      { id: 3, title: 'Machine Learning Fundamentals', channel: 'AI Explorers', views: '2.5M views', timestamp: '3 months ago', thumbnail: 'https://www.cibaky.com/wp-content/uploads/2015/12/placeholder-2.jpg' },
      { id: 4, title: 'Responsive Web Design Techniques', channel: 'CSS Ninjas', views: '1.8M views', timestamp: '2 months ago', thumbnail: 'https://www.cibaky.com/wp-content/uploads/2015/12/placeholder-2.jpg' },
      { id: 5, title: 'Blockchain Technology Explained', channel: 'Crypto Academy', views: '3.1M views', timestamp: '1 week ago', thumbnail: 'https://www.cibaky.com/wp-content/uploads/2015/12/placeholder-2.jpg' },
    ];
    
    const Player = () => {
      const [isDarkMode, setIsDarkMode] = useState(false);
      const [isPlaying, setIsPlaying] = useState(false);
      const [volume, setVolume] = useState(1);
      const [currentTime, setCurrentTime] = useState(0);
      const [currentQuality, setCurrentQuality] = useState('auto');
      const [duration, setDuration] = useState(0);
      const [showQualityMenu, setShowQualityMenu] = useState(false);
      const [showSpeedMenu, setShowSpeedMenu] = useState(false);
      const [currentSpeed, setCurrentSpeed] = useState(1);
      const [controlsVisible, setControlsVisible] = useState(true);
      const hideControlsTimeoutRef = useRef(null);
      const hoverTimeoutRef = useRef(null);
    
      const [isNewVideoProcessing, setIsNewVideoProcessing] = useState(false);
    
      const [videoTitle, setVideoTitle] = useState('Breakthrough Research in Chemistry: Inside the Lab with a Scientist');
      const [videoDescription, setVideoDescription] = useState('Join us as we step into the chemistry lab of a dedicated research scientist. In this video, we explore the intricate processes and cutting-edge techniques used to make groundbreaking discoveries. Watch as the scientist carefully conducts experiments, analyzes reactions, and works towards unlocking new insights in the field of chemistry. Whether you\'re a fellow researcher or simply curious about the scientific process, this behind-the-scenes look offers a fascinating glimpse into the world of chemistry research');
      const [videoTags, setVideoTags] = useState('video');
      const [videoLikes, setVideoLikes] = useState('12k');
      const [videoViews, setVideoViews] = useState('145k');
      const [videoUrls, setVideoUrls] = useState(["https://videos.pexels.com/video-files/3195394/3195394-sd_640_360_25fps.mp4"]);
      const [thumbnailPicture, setThumbnailPicture] = useState('https://media.istockphoto.com/id/1205722646/video/laboratory-beaker-and-test-tube-with-data-and-structural-formula-of-chemical-compounds-in-the.jpg?s=640x640&k=20&c=X5s3n9mKw4uVYdPtIZN2GcpBWut02dS7XRATZ2C5jxg=');
      const [availableQualities, setAvailableQualities] = useState(['auto']);
      const videoRef = useRef(null);
    
    
      useEffect(() => {
        const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
        setIsDarkMode(darkModeMediaQuery.matches);
    
        const handleChange = (e) => setIsDarkMode(e.matches);
        darkModeMediaQuery.addEventListener('change', handleChange);
    
        return () => darkModeMediaQuery.removeEventListener('change', handleChange);
      }, []);
    
      useEffect(() => {
        const video = videoRef.current;
        if (video) {
          video.addEventListener('timeupdate', handleTimeUpdate);
          video.addEventListener('loadedmetadata', handleLoadedMetadata);
          return () => {
            video.removeEventListener('timeupdate', handleTimeUpdate);
            video.removeEventListener('loadedmetadata', handleLoadedMetadata);
          };
        }
      }, []);
    
      useEffect(() => {
        // Check if videoUrls has been updated
        if (videoUrls.length > 0) {
          const videoSrc = videoUrls[0]; // Default to the first URL
          console.log('Video Source:', videoSrc);
          if (videoSrc) {
            const video = videoRef.current;
            video.src = videoSrc;
          } else {
            console.log('No valid video source found.');
          }
        } else {
          console.log('No video URLs available.');
        }
      }, [videoUrls]);
    
      useEffect(() => {
        const handleMouseEnter = () => {
          setControlsVisible(true);
          resetHideControlsTimeout();
          startHoverTimeout();
        };
    
        const handleMouseLeave = () => {
          resetHoverTimeout();
          startHideControlsTimeout();
        };
    
        const videoElement = videoRef.current;
    
        if (videoElement) {
          videoElement.addEventListener('mouseenter', handleMouseEnter);
          videoElement.addEventListener('mouseleave', handleMouseLeave);
        }
    
        startHideControlsTimeout();
    
        return () => {
          if (videoElement) {
            videoElement.removeEventListener('mouseenter', handleMouseEnter);
            videoElement.removeEventListener('mouseleave', handleMouseLeave);
          }
          clearTimeout(hideControlsTimeoutRef.current);
          clearTimeout(hoverTimeoutRef.current);
        };
      }, []);
    
      const resetHideControlsTimeout = () => {
        clearTimeout(hideControlsTimeoutRef.current);
      };
    
      const startHideControlsTimeout = () => {
        hideControlsTimeoutRef.current = setTimeout(() => {
          setControlsVisible(false);
        }, 5000); // Hide controls after 5 seconds
      };
    
      const resetHoverTimeout = () => {
        clearTimeout(hoverTimeoutRef.current);
      };
    
      const startHoverTimeout = () => {
        hoverTimeoutRef.current = setTimeout(() => {
          // Do nothing if still hovering after a long time
        }, 10000); // Long hover timeout (e.g., 10 seconds)
      };
    
      const handleTimeUpdate = () => {
        setCurrentTime(videoRef.current.currentTime);
      };
    
      const handleLoadedMetadata = () => {
        setDuration(videoRef.current.duration);
      };
    
      const togglePlay = () => {
        if (videoRef.current.paused) {
          videoRef.current.play();
          setIsPlaying(true);
        } else {
          videoRef.current.pause();
          setIsPlaying(false);
        }
      };
    
      const handleVolumeChange = (e) => {
        const newVolume = parseFloat(e.target.value);
        setVolume(newVolume);
        videoRef.current.volume = newVolume;
      };
    
      const handleSeek = (e) => {
        const time = parseFloat(e.target.value);
        setCurrentTime(time);
        videoRef.current.currentTime = time;
      };
    
      const formatTime = (time) => {
        const minutes = Math.floor(time / 60);
        const seconds = Math.floor(time % 60);
        return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
      };
    
      const handleQualityChange = (quality) => {
        const url = videoUrls.find((video) => video.includes(quality));
        if (url) {
          const video = videoRef.current;
          const wasPlaying = !video.paused; // Check if the video was playing before the quality change
          video.src = url;
    
          // Ensure the video resumes playing
          video.load();
          video.oncanplay = () => {
            if (wasPlaying) {
              video.play(); // Resume playback if it was playing before quality change
            }
          };
    
          setCurrentQuality(quality);
        }
        setShowQualityMenu(false);
      };
    
    
      const handleSpeedChange = (speed) => {
        setCurrentSpeed(speed);
        videoRef.current.playbackRate = speed;
        setShowSpeedMenu(false);
      };
    
      const handleRewind = () => {
        if (videoRef.current) {
          videoRef.current.currentTime -= 10;
        }
      };
    
      const handleForward = () => {
        if (videoRef.current) {
          videoRef.current.currentTime += 10;
        }
      };
    
      const toggleFullscreen = () => {
        if (videoRef.current) {
          if (document.fullscreenElement) {
            document.exitFullscreen();
          } else {
            videoRef.current.requestFullscreen();
          }
        }
      };
    
    
      return (
        <div className={`mt-12 min-h-screen ${isDarkMode ? 'dark' : ''}`}>
          <div>
    
            {/* Main content */}
            <main className={`transition-margin duration-300`}>
              <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
    
               
                {/* Video player */}
                <div className="aspect-w-16 aspect-h-9 mb-8 text-white -ml-3 -mr-3">
                  <div className="bg-black rounded-lg overflow-hidden relative">
                    <video
                      ref={videoRef}
                      className="w-full h-full max-h-[600px]"
                      poster={thumbnailPicture}
    
                    />
                    <div className={`absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-4 transition-opacity duration-300 ${controlsVisible ? 'opacity-100' : 'opacity-0'}`}>
                      <div className="flex flex-col space-y-2">
                        <div className="flex items-center justify-between">
                          <div className="flex space-x-2">
                            <button
                              onClick={togglePlay}
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label={isPlaying ? 'Pause' : 'Play'}
                            >
                              {isPlaying ? (
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z" />
                                </svg>
                              ) : (
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                                </svg>
                              )}
                            </button>
                            <button
                              onClick={handleRewind}
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label="Rewind 10 seconds"
                            >
                              <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0019 16V8a1 1 0 00-1.6-.8l-5.333 4zM4.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0011 16V8a1 1 0 00-1.6-.8l-5.334 4z" />
                              </svg>
                            </button>
                            <button
                              onClick={handleForward}
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label="Forward 10 seconds"
                            >
                              <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11.933 12.8a1 1 0 000-1.6l-5.334-4A1 1 0 005 8v8a1 1 0 001.6.8l5.333-4zM19.933 12.8a1 1 0 000-1.6l-5.334-4A1 1 0 0013 8v8a1 1 0 001.6.8l5.334-4z" />
                              </svg>
                            </button>
                          </div>
                          <div className="flex items-center space-x-2">
                            <div className="relative">
                              <button
                                onClick={() => setShowQualityMenu(!showQualityMenu)}
                                className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                                aria-label="Video quality"
                              >
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />
                                </svg>
                              </button>
                              {showQualityMenu && (
                                <div className="absolute bottom-full right-0 mb-2 w-32 bg-white dark:bg-gray-800 rounded-md shadow-lg">
                                  {availableQualities.map((quality) => (
                                    <button
                                      key={quality}
                                      onClick={() => handleQualityChange(quality)}
                                      className={`block w-full text-left px-4 py-2 text-sm ${currentQuality === quality ? 'bg-gray-100 dark:bg-gray-700' : ''
                                        } hover:bg-gray-100 dark:hover:bg-gray-700`}
                                    >
                                      {quality}
                                    </button>
                                  ))}
                                </div>
                              )}
                            </div>
                            <div className="relative">
                              <button
                                onClick={() => setShowSpeedMenu(!showSpeedMenu)}
                                className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                                aria-label="Playback speed"
                              >
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
                                </svg>
                              </button>
                              {showSpeedMenu && (
                                <div className="absolute bottom-full right-0 mb-2 w-32 bg-white dark:bg-gray-800 rounded-md shadow-lg">
                                  {[0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2].map((speed) => (
                                    <button
                                      key={speed}
                                      onClick={() => handleSpeedChange(speed)}
                                      className={`block w-full text-left px-4 py-2 text-sm ${currentSpeed === speed ? 'bg-gray-100 dark:bg-gray-700' : ''
                                        } hover:bg-gray-100 dark:hover:bg-gray-700`}
                                    >
                                      {speed}x
                                    </button>
                                  ))}
                                </div>
                              )}
                            </div>
                            <button
                              onClick={toggleFullscreen}
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label="Toggle fullscreen"
                            >
                              <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" />
                              </svg>
                            </button>
                          </div>
                        </div>
                        <div className="flex items-center space-x-2 text-white">
                          <span>{formatTime(currentTime)}</span>
                          <input
                            type="range"
                            min="0"
                            max={duration}
                            value={currentTime}
                            onChange={handleSeek}
                            className="flex-grow h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
                          />
                          <span>{formatTime(duration)}</span>
                        </div>
                        <div style={{ display: 'none' }} className="sm:flex items-center justify-between">
                          <div className="flex items-center space-x-2">
                            <button
                              onClick={() => setVolume(volume === 0 ? 1 : 0)}
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label={volume === 0 ? 'Unmute' : 'Mute'}
                            >
                              {volume === 0 ? (
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2" />
                                </svg>
                              ) : (
                                <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
                                </svg>
                              )}
                            </button>
                            <input
                              type="range"
                              min="0"
                              max="1"
                              step="0.1"
                              value={volume}
                              onChange={handleVolumeChange}
                              className="w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
                            />
                          </div>
                          <div className="flex items-center space-x-2">
                            <button
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label="Captions"
                            >
                              <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
                              </svg>
                            </button>
                            <button
                              className="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
                              aria-label="Settings"
                            >
                              <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                              </svg>
                            </button>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
    
                {/* Video info */}
                <div className="mb-8">
                  <h1 className="text-2xl font-bold mb-2">{videoTitle}</h1>
                  <div className="flex items-center justify-between flex-wrap gap-5">
                    <div className="flex items-center space-x-4">
                      <img className="h-10 w-10 rounded-full" src="https://thumbs.dreamstime.com/b/generic-person-gray-photo-placeholder-man-silhouette-white-background-144511705.jpg" alt="Channel avatar" />
                      <div>
                        <h2 className="font-semibold">Chemistry Fun</h2>
                        <p className="text-sm text-gray-500 dark:text-gray-400">1.2M subscribers</p>
                      </div>
                      <button className="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
                        Subscribe
                      </button>
                    </div>
                    <div className="flex items-center space-x-2">
                      <button className="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-white font-bold py-2 px-4 rounded inline-flex items-center">
                        <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
                        </svg>
                        <span>{videoLikes}</span>
                      </button>
                      <button  className="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-white font-bold py-2 px-4 rounded inline-flex items-center">
                        <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
                        </svg>
                        <span>Share</span>
                      </button>
                    </div>
                  </div>
                </div>
    
                {/* Video description */}
                <div className=" rounded-lg p-4 mb-8">
                  <p className="text-sm mb-2">{videoViews} views • May 15, 2023</p>
                  <p>
                    {videoDescription}
                  </p>
                  <button className="mt-2 text-blue-600 dark:text-blue-400 font-semibold">Show more</button>
                </div>
    
                {/* Recommended videos */}
                <div>
                  <h2 className="text-xl font-bold mb-4">Recommended videos</h2>
                  <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
                    {videos.map((video) => (
                      <div key={video.id} className="rounded-lg shadow-md overflow-hidden">
                        <img src={video.thumbnail} alt={video.title} className="w-full h-48 object-cover" />
                        <div className="p-4">
                          <h3 className="text-lg font-semibold mb-2">{video.title}</h3>
                          <p className="text-sm text-gray-600 dark:text-gray-400">{video.channel}</p>
                          <p className="text-sm text-gray-500 dark:text-gray-500">{video.views} • {video.timestamp}</p>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </main>
          </div>
        </div>
      );
    };
    
    export default Player;