├── .env
├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── logo.png
├── profile.jpg
└── vite.svg
├── src
├── App.jsx
├── assets
│ └── react.svg
├── components
│ ├── Home.jsx
│ ├── ListItems.jsx
│ ├── Navbar.jsx
│ ├── PlayingVideo.jsx
│ ├── Search.jsx
│ ├── SearchCard.jsx
│ ├── Sidebar.jsx
│ ├── SuggestedVideo.jsx
│ └── Video.jsx
├── context
│ └── AuthProvider.jsx
├── index.css
├── loader
│ ├── Loading.jsx
│ └── Time.jsx
├── main.jsx
└── utils
│ └── rapidapi.js
├── tailwind.config.js
└── vite.config.js
/.env:
--------------------------------------------------------------------------------
1 | VITE_YOUTUBE_API_KEY="6e79ac4f15msh1642f74265ebcb9p1faf31jsn3035a07d8221"
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react/jsx-no-target-blank': 'off',
16 | 'react-refresh/only-export-components': [
17 | 'warn',
18 | { allowConstantExport: true },
19 | ],
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yt-clone",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "axios": "^1.7.2",
14 | "js-abbreviation-number": "^1.4.0",
15 | "moment": "^2.30.1",
16 | "react": "^18.3.1",
17 | "react-avatar": "^5.0.3",
18 | "react-dom": "^18.3.1",
19 | "react-icons": "^5.2.1",
20 | "react-player": "^2.16.0",
21 | "react-router-dom": "^6.24.1"
22 | },
23 | "devDependencies": {
24 | "@types/react": "^18.3.3",
25 | "@types/react-dom": "^18.3.0",
26 | "@vitejs/plugin-react": "^4.3.1",
27 | "autoprefixer": "^10.4.19",
28 | "eslint": "^8.57.0",
29 | "eslint-plugin-react": "^7.34.2",
30 | "eslint-plugin-react-hooks": "^4.6.2",
31 | "eslint-plugin-react-refresh": "^0.4.7",
32 | "postcss": "^8.4.39",
33 | "tailwindcss": "^3.4.4",
34 | "vite": "^5.3.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LearnCodingOfficial/yt-clone/75d0c928312db9cb2ec9f8de26d852007ae88226/public/logo.png
--------------------------------------------------------------------------------
/public/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LearnCodingOfficial/yt-clone/75d0c928312db9cb2ec9f8de26d852007ae88226/public/profile.jpg
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Navbar from "./components/Navbar";
3 | import { Route, Routes } from "react-router-dom";
4 | import Home from "./components/Home";
5 | import Search from "./components/Search";
6 | import PlayingVideo from "./components/PlayingVideo";
7 | import { useAuth } from "./context/AuthProvider";
8 | import Loading from "./loader/Loading";
9 |
10 | function App() {
11 | const { loading } = useAuth();
12 | return (
13 |
14 | {loading && }
15 |
16 |
17 | } />
18 | } />
19 | } />
20 |
21 |
22 | );
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Sidebar from "./Sidebar.jsx";
3 | import Video from "./Video";
4 | import { useAuth } from "../context/AuthProvider.jsx";
5 | import ListItems from "./ListItems.jsx";
6 | function Home() {
7 | const { data, loading } = useAuth();
8 | console.log(data);
9 | return (
10 |
11 |
12 |
13 |
14 |
15 | {!loading &&
16 | data.map((item) => {
17 | if (item.type !== "video") return false;
18 | return ;
19 | })}
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | export default Home;
27 |
--------------------------------------------------------------------------------
/src/components/ListItems.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function ListItems() {
4 | const categories = [
5 | "All",
6 | "Music",
7 | "React routers",
8 | "Computer programming",
9 | "Reverberation",
10 | "Movie musicals",
11 | "India national cricket team",
12 | "News",
13 | "Mixes",
14 | "1990s",
15 | "Telugu cinema",
16 | "Live",
17 | "Dramedy",
18 | "Dubbing",
19 | "Indian soap opera",
20 | "Cricket",
21 | "Football",
22 | "Learn Coding",
23 | ];
24 | return (
25 |
26 |
27 | {categories.map((category) => {
28 | return (
29 |
33 | {category}
34 |
35 | );
36 | })}
37 |
38 |
39 | );
40 | }
41 |
42 | export default ListItems;
43 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Avatar from "react-avatar";
3 |
4 | import { AiOutlineMenu } from "react-icons/ai";
5 | import { CiSearch } from "react-icons/ci";
6 | import { IoMdMic } from "react-icons/io";
7 | import { RiVideoAddLine } from "react-icons/ri";
8 | import { AiOutlineBell } from "react-icons/ai";
9 |
10 | import logo from "../../public/logo.png";
11 | import profile from "../../public/profile.jpg";
12 | import { useNavigate } from "react-router-dom";
13 | function Navbar() {
14 | const [searchQuery, setSearchQuery] = useState("");
15 |
16 | const navigate = useNavigate();
17 |
18 | const searchQueryHandler = (event) => {
19 | if (
20 | (event?.key === "Enter" || event === "searchButton") &&
21 | searchQuery?.length > 0
22 | ) {
23 | navigate(`/search/${searchQuery}`);
24 | setSearchQuery("");
25 | }
26 | };
27 |
28 | return (
29 |
30 |
31 |
32 |

33 |
34 |
35 |
36 | setSearchQuery(e.target.value)}
41 | onKeyUp={searchQueryHandler}
42 | value={searchQuery}
43 | />
44 |
45 |
51 |
55 |
56 |
61 |
62 | );
63 | }
64 |
65 | export default Navbar;
66 |
--------------------------------------------------------------------------------
/src/components/PlayingVideo.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useParams } from "react-router-dom";
3 | import { fetchData } from "../utils/rapidapi";
4 | import ReactPlayer from "react-player";
5 | import { AiOutlineLike } from "react-icons/ai";
6 | import { abbreviateNumber } from "js-abbreviation-number";
7 | import SuggestedVideo from "./SuggestedVideo";
8 | import { BsFillCheckCircleFill } from "react-icons/bs";
9 |
10 | function PlayingVideo() {
11 | const [video, setVideo] = useState();
12 | const [realatedVideo, setRelativeVideo] = useState();
13 | const { id } = useParams();
14 |
15 | useEffect(() => {
16 | fetchVideoDetails();
17 | fetchRelatedVideo();
18 | }, [id]);
19 |
20 | const fetchVideoDetails = () => {
21 | fetchData(`video/details/?id=${id}`).then((res) => {
22 | console.log(res);
23 | setVideo(res);
24 | });
25 | };
26 | const fetchRelatedVideo = () => {
27 | fetchData(`video/related-contents/?id=${id}`).then((res) => {
28 | console.log(res);
29 | setRelativeVideo(res);
30 | });
31 | };
32 |
33 | return (
34 |
35 |
36 |
37 |
38 |
46 |
47 |
48 | {video?.title}
49 |
50 |
51 |
52 |
53 |
54 |

58 |
59 |
60 |
61 |
62 |
63 | {video?.author?.title}
64 | {video?.author?.badges[0]?.type === "VERIFIED_CHANNEL" && (
65 |
66 | )}
67 |
68 |
69 | {video?.author?.stats?.subscribersText}
70 |
71 |
72 |
73 | Subscribe
74 |
75 |
76 |
77 |
78 |
79 |
80 | {`${abbreviateNumber(video?.stats?.likes, 2)} Likes`}
81 |
82 |
83 | {`${abbreviateNumber(video?.stats?.views, 2)} Views`}
84 |
85 |
86 |
87 |
88 | {video?.description}
89 |
90 |
91 | {video?.stats?.comments}
Comments
92 |
93 |
94 |
95 | {realatedVideo?.contents?.map((item, index) => {
96 | if (item?.type !== "video") return false;
97 | return ;
98 | })}
99 |
100 |
101 |
102 | );
103 | }
104 |
105 | export default PlayingVideo;
106 |
--------------------------------------------------------------------------------
/src/components/Search.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useParams } from "react-router-dom";
3 | import { fetchData } from "../utils/rapidapi";
4 | import Sidebar from "./Sidebar.jsx";
5 | import SearchCard from "./SearchCard.jsx";
6 |
7 | function Search() {
8 | const [result, setResult] = useState();
9 | const { searchQuery } = useParams();
10 |
11 | useEffect(() => {
12 | fetchSearchResults();
13 | }, [searchQuery]);
14 |
15 | const fetchSearchResults = () => {
16 | fetchData(`search/?q=${searchQuery}`).then(({ contents }) => {
17 | console.log(contents);
18 | setResult(contents);
19 | });
20 | };
21 |
22 | return (
23 |
24 |
25 |
26 |
27 |
28 | {result?.map((item, index) => {
29 | if (item?.type !== "video") return false;
30 | return ;
31 | })}
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
39 | export default Search;
40 |
--------------------------------------------------------------------------------
/src/components/SearchCard.jsx:
--------------------------------------------------------------------------------
1 | import { abbreviateNumber } from "js-abbreviation-number";
2 | import React from "react";
3 | import { BsFillCheckCircleFill } from "react-icons/bs";
4 | import Time from "../loader/Time";
5 | import { Link } from "react-router-dom";
6 |
7 | function SearchCard({ video }) {
8 | console.log(video);
9 | return (
10 |
11 |
12 |
13 |
14 |

18 | {video?.lengthSeconds &&
}
19 |
20 |
21 |
22 | {video?.title}
23 |
24 |
25 | {video?.descriptionSnippet}
26 |
27 |
28 |
29 |
30 |

34 |
35 |
36 |
37 |
38 | {video?.author?.title}
39 | {video?.author?.badges[0]?.type === "VERIFIED_CHANNEL" && (
40 |
41 | )}
42 |
43 |
44 | {`${abbreviateNumber(
45 | video?.stats?.views,
46 | 2
47 | )} views`}
48 |
49 | .
50 |
51 | {video?.publishedTimeText}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | export default SearchCard;
63 |
--------------------------------------------------------------------------------
/src/components/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { GoHome } from "react-icons/go";
3 | import { SiYoutubeshorts } from "react-icons/si";
4 | import { MdOutlineSubscriptions, MdHistory } from "react-icons/md";
5 | import { PiUserSquareThin } from "react-icons/pi";
6 | import { IoGameControllerOutline } from "react-icons/io5";
7 | import { AiOutlineLike } from "react-icons/ai";
8 | import { FaChevronRight } from "react-icons/fa6";
9 | import { FaYoutube } from "react-icons/fa";
10 | import { SiYoutubestudio } from "react-icons/si";
11 | import { SiYoutubekids } from "react-icons/si";
12 | import { MdOutlineWatchLater } from "react-icons/md";
13 | import { SiYoutubemusic } from "react-icons/si";
14 | import { SiTrendmicro } from "react-icons/si";
15 | import { HiOutlineShoppingBag } from "react-icons/hi2";
16 | import { PiFilmSlateLight } from "react-icons/pi";
17 | import { CgMediaLive } from "react-icons/cg";
18 | import { SiYoutubegaming } from "react-icons/si";
19 | import { FaRegNewspaper } from "react-icons/fa";
20 | import { TfiCup } from "react-icons/tfi";
21 | import { PiLightbulbLight } from "react-icons/pi";
22 | import { SiStylelint } from "react-icons/si";
23 | import { MdPodcasts } from "react-icons/md";
24 | import { BiVideo } from "react-icons/bi";
25 | import { GiLinkedRings } from "react-icons/gi";
26 | function Sidebar() {
27 | const sidebarItems = [
28 | {
29 | id: 1,
30 | name: "Home",
31 | icon: ,
32 | },
33 | {
34 | id: 2,
35 | name: "Shorts",
36 | icon: ,
37 | },
38 | {
39 | id: 3,
40 | name: "Subscriptions",
41 | icon: ,
42 | },
43 | ];
44 | const sidebarItems2 = [
45 | {
46 | id: 1,
47 | name: "Your Channel",
48 | icon: ,
49 | },
50 | {
51 | id: 2,
52 | name: "History",
53 | icon: ,
54 | },
55 | {
56 | id: 3,
57 | name: "Playlists",
58 | icon: ,
59 | },
60 | {
61 | id: 4,
62 | name: "Your Videos",
63 | icon: ,
64 | },
65 | {
66 | id: 5,
67 | name: "Watch later",
68 | icon: ,
69 | },
70 | {
71 | id: 6,
72 | name: "Liked videos",
73 | icon: ,
74 | },
75 | ];
76 | const sidebarItems3 = [
77 | {
78 | id: 1,
79 | name: "Trending",
80 | icon: ,
81 | },
82 | {
83 | id: 2,
84 | name: "Shopping",
85 | icon: ,
86 | },
87 | {
88 | id: 3,
89 | name: "Music",
90 | icon: ,
91 | },
92 | {
93 | id: 4,
94 | name: "Films",
95 | icon: ,
96 | },
97 | {
98 | id: 5,
99 | name: "Live",
100 | icon: ,
101 | },
102 | {
103 | id: 6,
104 | name: "Gaming",
105 | icon: ,
106 | },
107 | {
108 | id: 7,
109 | name: "News",
110 | icon: ,
111 | },
112 | {
113 | id: 8,
114 | name: "Sport",
115 | icon: ,
116 | },
117 | {
118 | id: 9,
119 | name: "Courses",
120 | icon: ,
121 | },
122 | {
123 | id: 10,
124 | name: "Fashion & beauty",
125 | icon: ,
126 | },
127 | {
128 | id: 11,
129 | name: "Padcasts",
130 | icon: ,
131 | },
132 | ];
133 | const sidebarItems4 = [
134 | {
135 | id: 1,
136 | name: "Youtube Premium",
137 | icon: ,
138 | },
139 | {
140 | id: 2,
141 | name: "Youtube Studio",
142 | icon: ,
143 | },
144 | {
145 | id: 3,
146 | name: "Youtube Music",
147 | icon: ,
148 | },
149 | {
150 | id: 4,
151 | name: "Youtube Kids",
152 | icon: ,
153 | },
154 | ];
155 | return (
156 |
157 | {/* Home */}
158 |
159 | {sidebarItems.map((item) => {
160 | return (
161 |
165 |
{item.icon}
166 |
{item.name}
167 |
168 | );
169 | })}
170 |
171 |
172 |
173 | {/* You */}
174 |
175 |
176 |
You
177 |
178 |
179 | {sidebarItems2.map((item) => {
180 | return (
181 |
185 |
{item.icon}
186 |
{item.name}
187 |
188 | );
189 | })}
190 |
191 |
192 |
193 | {/* Explore */}
194 |
195 |
196 |
Explore
197 |
198 | {sidebarItems3.map((item) => {
199 | return (
200 |
204 |
{item.icon}
205 |
{item.name}
206 |
207 | );
208 | })}
209 |
210 |
211 |
212 | {/* More section */}
213 |
214 |
215 |
More From Youtube
216 |
217 | {sidebarItems4.map((item) => {
218 | return (
219 |
223 |
224 | {item.icon}
225 |
226 |
{item.name}
227 |
228 | );
229 | })}
230 |
231 |
232 |
233 |
234 | About Press Copyright
Contact us Creators
Advertise
235 | Developers
236 | Terms Privacy Policy & Safety
How YouTube works{" "}
237 |
Test new features
238 |
239 |
240 |
© 2024 Learn Coding
241 |
242 | );
243 | }
244 |
245 | export default Sidebar;
246 |
--------------------------------------------------------------------------------
/src/components/SuggestedVideo.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import Time from "../loader/Time";
4 | import { BsFillCheckCircleFill } from "react-icons/bs";
5 | import { abbreviateNumber } from "js-abbreviation-number";
6 |
7 | function SuggestedVideo({ video }) {
8 | console.log(video);
9 | return (
10 |
11 |
12 |
13 |
14 |

18 | {video?.lengthSeconds &&
}
19 |
20 |
21 |
22 | {video?.title}
23 |
24 |
25 | {video?.author?.title}
26 | {video?.author?.badges[0]?.type === "VERIFIED_CHANNEL" && (
27 |
28 | )}
29 |
30 |
31 | {`${abbreviateNumber(video?.stats?.views, 2)} views`}
32 |
33 | .
34 |
35 | {video?.publishedTimeText}
36 |
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default SuggestedVideo;
45 |
--------------------------------------------------------------------------------
/src/components/Video.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import Time from "../loader/Time";
4 | import { BsFillCheckCircleFill } from "react-icons/bs";
5 | import { abbreviateNumber } from "js-abbreviation-number";
6 |
7 | function Video({ video }) {
8 | console.log(video);
9 |
10 | return (
11 |
12 |
13 |
14 | {/* thumbnail & duration */}
15 |
16 |

21 | {video?.lengthSeconds &&
}
22 |
23 | {/* channerl logo & title */}
24 |
25 |
26 |
27 |

32 |
33 |
34 |
35 |
36 | {video?.title}
37 |
38 |
39 | {video?.author?.title}
40 | {video?.author?.badges[0]?.type === "VERIFIED_CHANNEL" && (
41 |
42 | )}
43 |
44 |
45 | {`${abbreviateNumber(
46 | video?.stats?.views,
47 | 2
48 | )} views`}
49 |
50 | .
51 |
52 | {video?.publishedTimeText}
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | export default Video;
63 |
--------------------------------------------------------------------------------
/src/context/AuthProvider.jsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext, useEffect, useState } from "react";
2 | import { fetchData } from "../utils/rapidapi";
3 |
4 | export const AuthContext = createContext();
5 |
6 | export default function AuthProvider({ children }) {
7 | const [loading, setLoading] = useState(false);
8 | const [data, setData] = useState([]);
9 | const [value, setValue] = useState("New");
10 |
11 | useEffect(() => {
12 | fetchAlldata(value);
13 | }, [value]);
14 |
15 | const fetchAlldata = (query) => {
16 | setLoading(true);
17 | fetchData(`search/?q=${query}`).then(({ contents }) => {
18 | console.log(contents);
19 | setData(contents);
20 | setLoading(false);
21 | });
22 | };
23 | return (
24 |
25 | {children}
26 |
27 | );
28 | }
29 |
30 | export const useAuth = () => useContext(AuthContext);
31 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/loader/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function Loading() {
4 | return (
5 |
6 |
7 | ...
8 |
9 |
10 | );
11 | }
12 |
13 | export default Loading;
14 |
--------------------------------------------------------------------------------
/src/loader/Time.jsx:
--------------------------------------------------------------------------------
1 | import moment from "moment";
2 | import React from "react";
3 |
4 | function Time({ time }) {
5 | const videoTime = moment()?.startOf("day")?.seconds(time)?.format("H:mm:ss");
6 | return (
7 |
8 |
9 | {videoTime}
10 |
11 |
12 | );
13 | }
14 |
15 | export default Time;
16 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App.jsx";
4 | import "./index.css";
5 | import AuthProvider from "./context/AuthProvider.jsx";
6 | import { BrowserRouter } from "react-router-dom";
7 |
8 | ReactDOM.createRoot(document.getElementById("root")).render(
9 |
10 |
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/src/utils/rapidapi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const API_KEY=import.meta.env.VITE_YOUTUBE_API_KEY;
4 | const BASE_URL="https://youtube138.p.rapidapi.com"
5 |
6 | const options = {
7 | headers: {
8 | 'x-rapidapi-key': API_KEY,
9 | 'x-rapidapi-host': 'youtube138.p.rapidapi.com'
10 | }
11 | };
12 |
13 | export const fetchData=async(url)=>{
14 | try{
15 | const {data} = await axios.get(`${BASE_URL}/${url}`,options);
16 | return data;
17 | }catch(error){
18 | console.error("error fetching api data: ",error);
19 | throw error;
20 | }
21 | }
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------