Create A YouTube Homepage Clone in ReactJS and Tailwind CSS

6

Create A YouTube Homepage Clone in Tailwind CSS and React.js - Light Mode

Creating a clone of the YouTube homepage can be both enjoyable and helpful for enhancing your front-end development skills. This project offers a chance to work on a familiar design while getting practical experience with commonly used tools like React.js and Tailwind CSS. It also helps you understand how modern web applications are structured and styled.

In this blog post, I’ll guide you through creating a responsive YouTube homepage clone using React.js and Tailwind CSS. This project will replicate key features of YouTube’s design, such as a navbar with search, a grid layout for videos, a collapsible sidebar, and options for dark or light themes.

Demo of YouTube Homepage Clone in React.js & Tailwind

Tools and Libraries

  • React.js: Used for building the user interface.
  • Tailwind CSS: Used for styling the components.
  • Lucide React: Used for icons in the sidebar and other components.

Setting Up the Project

Before we start making YouTube homepage clone with React.js and Tailwind CSS, make sure you have Node.js installed on your computer. If you don’t have it, you can download and install it from the official Node.js website.

After installing Node.js, follow these easy steps to set up your project:

Create a Project Folder:

  • Make a new folder, for instance, “youtube-homepage-clone”.
  • Open this folder in your VS Code editor.

Initialize the Project:

Open your terminal by pressing Ctrl + J and then use Vite to create a new React app with this command:

npm create vite@latest ./ -- --template react

Install necessary dependencies:

npm install

Install Tailwind CSS:

Install Tailwind CSS, PostCSS, and Autoprefixer:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Install Lucide React:

Add icons with Lucide React:

npm install lucide-react

Configure Tailwind CSS:

Replace the code in tailwind.config.js with the provided configuration.

/** @type {import('tailwindcss').Config} */
export default {
  darkMode: "class",
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Start the Development Server:

npm run dev

If your project is up and running in your browser, congratulations! You’ve successfully set up your project. Now, let’s move on to the next step.

Modify CSS Files:

  • Remove the default App.css file.
  • Replace the content of index.css with the provided code.
/* Importing Google Font - Open Sans */
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap");

@tailwind base;
@tailwind components;
@tailwind utilities;

* {
  font-family: "Open Sans", sans-serif;
}

.custom_scrollbar {
  scrollbar-color: #999 transparent;
}

aside .custom_scrollbar {
  scrollbar-width: none;
  scrollbar-gutter: stable;
}

aside .custom_scrollbar:hover {
  scrollbar-width: thin;
}

.no_scrollbar {
  scrollbar-width: none;
}

.no_scrollbar::-webkit-scrollbar {
  display: none;
}

@media (max-width: 768px) {
  .custom_scrollbar.hide_thumb {
    scrollbar-color: transparent transparent;
  }
}

Assets Folder:

Download the assets folder and replace the existing one in your project directory. This folder contains the logo and user image used on this YouTube homepage project.

Creating the Components

Within the src directory of your project, organize your files by creating three different folders: “layouts”, “components”, and “constants”. Inside these folders, create the following files:

  • layouts/Navbar.jsx
  • layouts/Sidebar.jsx
  • components/CategoryPill.jsx
  • components/VideoItem.jsx
  • constants/index.js

Adding the Codes

Add the respective code to each newly created file. These files define the layout, functionality, and constants used in the website.

In layouts/Navbar.jsx, add the following code. This file defines the layout for the navigation bar of our application.

import { Menu, Mic, MoonStar, Search, Sun } from "lucide-react";
import Logo from "../assets/logo.png";
import UserImg from "../assets/user.jpg";
import { useEffect, useState } from "react";

const Navbar = ({ toggleSidebar }) => {
  // Initialize dark mode state based on localStorage value
  const [isDarkMode, setIsDarkMode] = useState(() => {
    const savedMode = localStorage.getItem("darkMode");
    return savedMode ? JSON.parse(savedMode) : false;
  });

  // Effect to update body class and localStorage when dark mode state changes
  useEffect(() => {
    document.body.classList[isDarkMode ? "add" : "remove"]("dark");
    localStorage.setItem("darkMode", JSON.stringify(isDarkMode));
  }, [isDarkMode]);

  // Function to toggle dark mode state
  const toggleDarkMode = () => {
    setIsDarkMode((prevMode) => !prevMode);
  };

  return (
    <header className="sticky top-0 z-10 bg-white dark:bg-neutral-900">
      <nav className="flex items-center justify-between py-2 pb-5 px-4">
        {/* Rendering left section of the navbar */}
        <HeaderLeftSection toggleSidebar={toggleSidebar} />

        {/* Search input and mic section */}
        <div className="h-10 flex gap-3 w-[600px] max-lg:w-[500px] max-md:hidden">
          <form action="#" className="flex w-full">
            <input
              className="border border-neutral-300 w-full h-full rounded-l-full px-4 outline-none focus:border-blue-500 dark:bg-neutral-900 dark:border-neutral-500 dark:focus:border-blue-500 dark:text-neutral-300"
              type="search"
              placeholder="Search"
              required
            />
            <button className="border border-neutral-300 px-5 border-l-0 rounded-r-full hover:bg-neutral-100 dark:border-neutral-500 hover:dark:bg-neutral-700">
              <Search className="dark:text-neutral-400" />
            </button>
          </form>
          <button className="p-2 rounded-full bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-800 hover:dark:bg-neutral-700">
            <Mic className="dark:text-neutral-400" />
          </button>
        </div>

        {/* User and dark mode toggle section */}
        <div className="flex items-center gap-4">
          <button className="p-2 rounded-full md:hidden hover:bg-neutral-200 hover:dark:bg-neutral-700">
            <Search className="dark:text-neutral-400" />
          </button>
          <button
            onClick={toggleDarkMode}
            className="p-2 rounded-full hover:bg-neutral-200 hover:dark:bg-neutral-700"
          >
            {isDarkMode ? (
              <Sun className="dark:text-neutral-400" />
            ) : (
              <MoonStar className="dark:text-neutral-400" />
            )}
          </button>
          <img
            className="w-8 h-8 rounded-full cursor-pointer"
            src={UserImg}
            alt="User Image"
          />
        </div>
      </nav>
    </header>
  );
};

// Component for the left section of the navbar
export const HeaderLeftSection = ({ toggleSidebar }) => {
  return (
    <div className="flex gap-4 items-center">
      <button
        onClick={toggleSidebar}
        className="p-2 rounded-full hover:bg-neutral-200 hover:dark:bg-neutral-700"
      >
        <Menu className="dark:text-neutral-400" />
      </button>
      <a className="flex items-center gap-2" href="#">
        <img src={Logo} width={32} alt="Logo" />
        <h2 className="text-xl font-bold dark:text-neutral-300">CnTube</h2>
      </a>
    </div>
  );
};

export default Navbar;

In layouts/Sidebar.jsx, add the following code. This file defines the layout for the sidebar bar of our application.

import React from "react";
import { sidebarLinks } from "../constants";
import { Home, Video, TvMinimal, UserRound, History, Clock4, Flame, Music, Gamepad2, Trophy, TvMinimalPlay, ListMusic, Tv, Settings, Flag, CircleHelp, MessageSquareWarning } from "lucide-react";
import { HeaderLeftSection } from "./Navbar";

// Mapping icon names to Lucide React components
const iconComponents = { Home, Video, TvMinimal, UserRound, History, Clock4, Flame, Music, Gamepad2, Trophy, TvMinimalPlay, ListMusic, Tv, Settings, Flag, CircleHelp, MessageSquareWarning };

const Sidebar = ({ toggleSidebar, isSidebarOpen }) => {
  return (
    <aside
      className={`${isSidebarOpen
          ? "max-md:left-0 w-[280px] px-3"
          : "max-md:left-[-100%] w-0 px-0"
        } max-md:absolute max-md:h-screen max-md:top-0 bg-white overflow-hidden z-30 dark:bg-neutral-900 max-md:transition-all max-md:duration-200`}
    >
      {/* Header section for mobile */}
      <div className="md:hidden pb-5 pt-2 px-1 sticky top-0 bg-white dark:bg-neutral-900">
        <HeaderLeftSection toggleSidebar={toggleSidebar} />
      </div>

      <div className="overflow-y-auto h-[calc(100vh-70px)] custom_scrollbar pb-6">
        {/* Mapping through sidebarLinks to render categories and links */}
        {sidebarLinks.map((category, catIndex) => (
          <div key={catIndex}>
            {/* Render category title if exists */}
            {category.categoryTitle && (
              <h4 className="text-[15px] font-semibold mb-2 ml-2 mt-4 dark:text-neutral-300">
                {category.categoryTitle}
              </h4>
            )}

            {/* Mapping through links within each category */}
            {category.links.map((link, index) => {
              const IconComponent = iconComponents[link.icon];
              return (
                <React.Fragment key={`${catIndex}-${index}`}>
                  <Link link={link} IconComponent={IconComponent} />

                  {/* Render divider line if not last link in category */}
                  {index === category.links.length - 1 &&
                    catIndex !== sidebarLinks.length - 1 && (
                      <div className="h-[1px] my-2.5 bg-neutral-200 dark:bg-neutral-700"></div>
                    )}
                </React.Fragment>
              );
            })}
          </div>
        ))}
      </div>
    </aside>
  );
};

// Link component within the sidebar
export const Link = ({ link, IconComponent }) => {
  return (
    <a
      href={link.url}
      className={`flex text-[15px] items-center py-2.5 px-3 rounded-lg hover:bg-neutral-200 mb-1 whitespace-nowrap dark:text-neutral-300 dark:hover:bg-neutral-500`}
    >
      {IconComponent && <IconComponent className="mr-2.5 h-5 w-5" />}
      {link.title}
    </a>
  );
};

export default Sidebar;

In components/CategoryPill.jsx, add the following code. This component code is used for rendering category pills.

const CategoryPill = ({ category }) => {
  return (
    <div className={`text-[15px] font-medium whitespace-nowrap rounded-lg px-3 py-1 ${category === "All" ? 'bg-black text-white hover:bg-neutral-950 dark:bg-white dark:text-black' : 'bg-neutral-200 text-black hover:bg-neutral-300 dark:text-neutral-300 dark:bg-neutral-700 dark:hover:bg-neutral-600'} cursor-pointer`}>{category}</div>
  )
}

export default CategoryPill

In components/VideoItem.jsx, add the following code. This component handles the rendering of individual video items within our application.

const VideoItem = ({ video }) => {
  return (
    <a className="group" href="#">
      <div className="relative">
        <img className="rounded-lg aspect-video" src={video.thumbnailURL} alt={video.title} />
        <p className="absolute bottom-2 right-2 text-sm bg-black bg-opacity-50 text-white px-1.5 font-medium rounded-md">
          {video.duration}
        </p>
      </div>

      <div className="flex gap-3 py-3 px-2">
        <img className="h-9 w-9 rounded-full" src={video.channel.logo} alt={video.channel.name} />
        <div>
          <h2 className="group-hover:text-blue-500 font-semibold leading-snug line-clamp-2 dark:text-neutral-300" title={video.title}>
            {video.title}
          </h2>
          <p className="text-sm mt-1 text-neutral-700 hover:text-neutral-500 dark:text-neutral-300">
            {video.channel.name}
          </p>
          <p className="text-sm text-neutral-700 dark:text-neutral-300">
            {video.views} Views • {video.postedAt}
          </p>
        </div>
      </div>
    </a>
  )
}

export default VideoItem

In constants/index.js, include the following code. This file serves as a main location for defining and managing constants used throughout the website, ensuring consistency and maintainability.

// Categories array
export const categories = ["All", "Website", "Music", "Gaming", "Node.js", "React.js", "TypeScript", "Coding", "Data analysis", "JavaScript", "Web design", "Tailwind", "HTML", "CSS", "Next.js", "Express.js"];

// Sidebar navigation links
export const sidebarLinks = [
  {
    links: [
      {
        icon: "Home",
        title: "Home",
        url: "#",
      },
      {
        icon: "Video",
        title: "Shorts",
        url: "#",
      },
      {
        icon: "TvMinimal",
        title: "Subscriptions",
        url: "#",
      },
    ],
  },
  {
    categoryTitle: "You",
    links: [
      {
        icon: "UserRound",
        title: "Your channel",
        url: "#",
      },
      {
        icon: "History",
        title: "History",
        url: "#",
      },
      {
        icon: "Clock4",
        title: "Watch later",
        url: "#",
      },
    ],
  },
  {
    categoryTitle: "Explore",
    links: [
      {
        icon: "Flame",
        title: "Trending",
        url: "#",
      },
      {
        icon: "Music",
        title: "Music",
        url: "#",
      },
      {
        icon: "Gamepad2",
        title: "Gaming",
        url: "#",
      },
      {
        icon: "Trophy",
        title: "Sports",
        url: "#",
      },
    ],
  },
  {
    categoryTitle: "More from YouTube",
    links: [
      {
        icon: "TvMinimalPlay",
        title: "YouTube Pro",
        url: "#",
      },
      {
        icon: "ListMusic",
        title: "YouTube Music",
        url: "#",
      },
      {
        icon: "Tv",
        title: "YouTube Kids",
        url: "#",
      },
    ],
  },
  {
    links: [
      {
        icon: "Settings",
        title: "Settings",
        url: "#",
      },
      {
        icon: "Flag",
        title: "Report",
        url: "#",
      },
      {
        icon: "CircleHelp",
        title: "Help",
        url: "#",
      },
      {
        icon: "MessageSquareWarning",
        title: "Feedback",
        url: "#",
      },
    ],
  },
];

// Video data
export const videos = [
  {
    id: "1",
    title: "Top 10 Easy To Create JavaScript Games For Beginners",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "27K",
    postedAt: "4 months ago",
    duration: "10:03",
    thumbnailURL: "https://i.ytimg.com/vi/OORUHkgg4IM/maxresdefault.jpg",
    videoURL: "https://youtu.be/OORUHkgg4IM",
  },
  {
    id: "2",
    title:
      "Create A Responsive Website with Login & Registration Form in HTML CSS and JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "68K",
    postedAt: "9 months ago",
    duration: "29:43",
    thumbnailURL: "https://i.ytimg.com/vi/YEloDYy3DTg/maxresdefault.jpg",
    videoURL: "https://youtu.be/YEloDYy3DTg",
  },
  {
    id: "3",
    title: "Build Hangman Game in HTML CSS and JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "57K",
    postedAt: "11 months ago",
    duration: "38:45",
    thumbnailURL: "https://i.ytimg.com/vi/hSSdc8vKP1I/maxresdefault.jpg",
    videoURL: "https://youtu.be/hSSdc8vKP1I",
  },
  {
    id: "4",
    title: "Responsive Admin Dashboard Panel in HTML CSS and JavaScript",
    channel: {
      name: "CodingLab",
      url: "https://www.youtube.com/@CodingLabYT",
      logo: "https://yt3.googleusercontent.com/uITV5E7auiZMDD_BwhVRJMHXXY6qQc0GqBgVyP5LWYTmeRlUP2Dc945UlIbODvztd96ReOts=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "161K",
    postedAt: "1 year ago",
    duration: "1:37:13",
    thumbnailURL: "https://i.ytimg.com/vi/AyV954yKRSw/maxresdefault.jpg",
    videoURL: "https://youtu.be/AyV954yKRSw",
  },
  {
    id: "5",
    title: "Make A Flipping Card UI Design in HTML & CSS",
    channel: {
      name: "CodingLab",
      url: "https://www.youtube.com/@CodingLabYT",
      logo: "https://yt3.googleusercontent.com/uITV5E7auiZMDD_BwhVRJMHXXY6qQc0GqBgVyP5LWYTmeRlUP2Dc945UlIbODvztd96ReOts=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "85K",
    postedAt: "2 months ago",
    duration: "12:24",
    thumbnailURL: "https://i.ytimg.com/vi/20Qb7pNMv-4/maxresdefault.jpg",
    videoURL: "https://youtu.be/20Qb7pNMv-4",
  },
  {
    id: "6",
    title:
      "Easy way to do Multiple File Uploading using HTML CSS and JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "7.4K",
    postedAt: "3 weeks ago",
    duration: "30:20",
    thumbnailURL: "https://i.ytimg.com/vi/_RSaI2CxlXU/maxresdefault.jpg",
    videoURL: "https://youtu.be/_RSaI2CxlXU",
  },
  {
    id: "7",
    title: "How to make Responsive Card Slider in HTML CSS & JavaScript",
    channel: {
      name: "CodingLab",
      url: "https://www.youtube.com/@CodingLabYT",
      logo: "https://yt3.googleusercontent.com/uITV5E7auiZMDD_BwhVRJMHXXY6qQc0GqBgVyP5LWYTmeRlUP2Dc945UlIbODvztd96ReOts=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "42K",
    postedAt: "1 year ago",
    duration: "23:45",
    thumbnailURL: "https://i.ytimg.com/vi/qOO6lVMhmGc/maxresdefault.jpg",
    videoURL: "https://youtu.be/qOO6lVMhmGc",
  },
  {
    id: "8",
    title: "How to Make Chrome Extension in HTML CSS & JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "24K",
    postedAt: "1 year ago",
    duration: "19:27",
    thumbnailURL: "https://i.ytimg.com/vi/coj-l7IrwGU/maxresdefault.jpg",
    videoURL: "https://youtu.be/coj-l7IrwGU",
  },
  {
    id: "9",
    title: "How to make Responsive Image Slider in HTML CSS and JavaScript",
    channel: {
      name: "CodingLab",
      url: "https://www.youtube.com/@CodingLabYT",
      logo: "https://yt3.googleusercontent.com/uITV5E7auiZMDD_BwhVRJMHXXY6qQc0GqBgVyP5LWYTmeRlUP2Dc945UlIbODvztd96ReOts=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "1M",
    postedAt: "1 year ago",
    duration: "37:13",
    thumbnailURL: "https://i.ytimg.com/vi/q4RgxiDM6v0/maxresdefault.jpg",
    videoURL: "https://youtu.be/q4RgxiDM6v0",
  },
  {
    id: "10",
    title: "Create Responsive Image Slider in HTML CSS and JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "157K",
    postedAt: "9 months ago",
    duration: "25:27",
    thumbnailURL: "https://i.ytimg.com/vi/PsNaoDhzQm0/maxresdefault.jpg",
    videoURL: "https://youtu.be/PsNaoDhzQm0",
  },
  {
    id: "11",
    title: "Create Text Typing Effect in HTML CSS & Vanilla JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "17K",
    postedAt: "10 months ago",
    duration: "9:27",
    thumbnailURL: "https://i.ytimg.com/vi/DLs1X9T1GcY/maxresdefault.jpg",
    videoURL: "https://youtu.be/DLs1X9T1GcY",
  },
  {
    id: "12",
    title: "Build A Responsive Calculator in HTML CSS & JavaScript",
    channel: {
      name: "CodingLab",
      url: "https://www.youtube.com/@CodingLabYT",
      logo: "https://yt3.googleusercontent.com/uITV5E7auiZMDD_BwhVRJMHXXY6qQc0GqBgVyP5LWYTmeRlUP2Dc945UlIbODvztd96ReOts=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "30K",
    postedAt: "2 years ago",
    duration: "11:13",
    thumbnailURL: "https://i.ytimg.com/vi/cHkN82X3KNU/maxresdefault.jpg",
    videoURL: "https://youtu.be/cHkN82X3KNU",
  },
  {
    id: "13",
    title: "Create A Draggable Card Slider in HTML CSS and Vanilla JavaScript",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "14.2K",
    postedAt: "4 days ago",
    duration: "16:24",
    thumbnailURL: "https://i.ytimg.com/vi/6QE8dXq9SOE/maxresdefault.jpg",
    videoURL: "https://youtu.be/6QE8dXq9SOE",
  },
  {
    id: "14",
    title: "Build A Currency Converter using ReactJS",
    channel: {
      name: "CodingNepal",
      url: "https://www.youtube.com/@CodingNepal",
      logo: "https://yt3.googleusercontent.com/DRtVBjk2Noax94hHqr8yCcEjhNUhHRvyzBE3qS9WWilnE1-uQQNVnQd8mdG9h_IvNZCRApZSQw=s176-c-k-c0x00ffffff-no-rj",
    },
    views: "7.2K",
    postedAt: "2 weeks ago",
    duration: "39:43",
    thumbnailURL: "https://i.ytimg.com/vi/0_Lwi5ucGwM/maxresdefault.jpg",
    videoURL: "https://youtu.be/0_Lwi5ucGwM",
  },
];

Replace the content of src/App.jsx with the provided code. It imports and renders the necessary components, such as the Navbar and Sidebar, to create a layout resembling the YouTube homepage.

import { useEffect, useState } from "react";
import CategoryPill from "./components/CategoryPill";
import Navbar from "./components/Navbar";
import Sidebar from "./components/Sidebar";
import VideoItem from "./components/VideoItem";
import { categories, videos } from "./constants";

export default function App() {
  // State variable to track sidebar visibility
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);

  // Function to toggle sidebar visibility
  const toggleSidebar = () => {
    setIsSidebarOpen(!isSidebarOpen);
  };

  // Effect hook to show sidebar on large devices initially
  useEffect(() => {
    if (window.innerWidth >= 768) setIsSidebarOpen(true);
  }, []);

  return (
    <div className="max-h-screen flex flex-col overflow-hidden dark:bg-neutral-900">
      <Navbar toggleSidebar={toggleSidebar} />
      <div className="flex overflow-auto">
        <Sidebar toggleSidebar={toggleSidebar} isSidebarOpen={isSidebarOpen} />

        {/* Overlay for mobile to close sidebar */}
        <div
          onClick={toggleSidebar}
          className={`md:hidden ${
            !isSidebarOpen && "opacity-0 pointer-events-none"
          } transition-all bg-black bg-opacity-50 h-screen w-full fixed left-0 top-0 z-20`}
        ></div>

        <div
          className={`w-full px-4 overflow-x-hidden custom_scrollbar ${
            isSidebarOpen && "hide_thumb"
          }`}
        >
          {/* Category list */}
          <div className="sticky bg-white top-0 z-10 pb-3 flex gap-3 overflow-y-auto no_scrollbar dark:bg-neutral-900">
            {categories.map((category) => (
              <CategoryPill key={category} category={category} />
            ))}
          </div>

          {/* Video grid */}
          <div className="grid gap-4 grid-cols-[repeat(auto-fill,minmax(300px,1fr))] mt-5 pb-6">
            {videos.map((video) => (
              <VideoItem key={video.id} video={video} />
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

Once you’ve completed all the steps, congratulations! You should now be able to see the YouTube homepage clone in your browser.

Create A YouTube Homepage Clone in Tailwind CSS and React.js - Dark Mode

Conclusion and final words

In conclusion, creating a YouTube homepage clone using React.js and Tailwind CSS is a great way to enhance your web development skills. By following the steps outlined in this blog, you have successfully created a clone of the YouTube homepage on your own.

If you encounter any issues while working on your YouTube homepage clone project, you can download the source code files for free by clicking the “Download” button. You can also view a live demo by clicking the “View Live” button.”

After downloading the zip file, unzip it and open the “youtube-homepage-clone” folder in VS Code. Then, open the terminal by pressing Ctrl + J and run these commands to view your project in the browser: npm install and npm run dev.

Previous article☕ Create A Responsive Coffee Website in HTML CSS & JavaScript
Next articleMake A Simple Login Form in HTML and CSS only

6 COMMENTS

  1. Do you have any idea how I can make this project able to view video files from internal storage and play? I really want to do this project to learn more.

LEAVE A REPLY

Please enter your comment!
Please enter your name here