├── .env ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── Screenshot (61).png ├── favicon.ico ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.js ├── Requests.js ├── components │ ├── Main.jsx │ ├── Movie.jsx │ ├── Navbar.jsx │ ├── ProtectedRoute.jsx │ ├── Row.jsx │ └── SavedShows.jsx ├── context │ └── AuthContext.js ├── firebase.js ├── index.css ├── index.js └── pages │ ├── Account.jsx │ ├── Home.jsx │ ├── Login.jsx │ └── Signup.jsx └── tailwind.config.js /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_FIREBASE_API_KEY=YOUR_HIDDEN_KEY 2 | REACT_APP_FIREBASE_AUTH_DOMAIN=YOUR_HIDDEN_KEY 3 | REACT_APP_FIREBASE_PROJECT_ID=YOUR_HIDDEN_KEY 4 | REACT_APP_FIREBASE_STORAGE_BUCKET=YOUR_HIDDEN_KEY 5 | REACT_APP_MESSAGING_SENDER=YOUR_HIDDEN_KEY 6 | REACT_APP_APP_ID=YOUR_HIDDEN_KEY 7 | REACT_APP_IMDB_API_KEY=YOUR_HIDDEN_KEY 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

A simples Netflix clone with user authentication

2 |
3 |
4 |

Here i presents a simple NETFLIX clone using React Js , Tailwind CSS , little bit of API And Firebase for the backend and userauthentication with account details and also showing the liked movies by us and we can also remove the saved or liked movies

5 |
6 | 7 | 8 |

You can see the Clone created by me by clicking here

9 |

Note:Also Please add you keys and change the Env variables First

10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.2.4", 10 | "firebase": "^9.16.0", 11 | "postcss": "^8.4.21", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-icons": "^4.7.1", 15 | "react-router-dom": "^6.7.0", 16 | "react-scripts": "^5.0.1", 17 | "tailwind-scrollbar-hide": "^1.1.7", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "tailwindcss": "^3.2.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/Screenshot (61).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishnubansal001/NetflixClone-ReactJs-TailwindCSS-FireBase/901f7c6a11b332986f7703e39bff617d2fe84a69/public/Screenshot (61).png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishnubansal001/NetflixClone-ReactJs-TailwindCSS-FireBase/901f7c6a11b332986f7703e39bff617d2fe84a69/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | Netflix Clone by VB 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from './components/Navbar'; 3 | import { Route, Routes } from 'react-router-dom'; 4 | import Home from './pages/Home'; 5 | import { AuthContextProvider } from './context/AuthContext'; 6 | import Login from './pages/Login'; 7 | import Signup from './pages/Signup'; 8 | import Account from './pages/Account'; 9 | import ProtectedRoute from './components/ProtectedRoute'; 10 | 11 | function App() { 12 | return ( 13 | <> 14 | 15 | 16 | 17 | }> 18 | }> 19 | }> 20 | }> 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/Requests.js: -------------------------------------------------------------------------------- 1 | const key = process.env.REACT_APP_IMDB_API_KEY; 2 | const requests = { 3 | requestPopular: `https://api.themoviedb.org/3/movie/popular?api_key=${key}&language=en-US&page=1`, 4 | requestTopRated: `https://api.themoviedb.org/3/movie/top_rated?api_key=${key}&language=en-US&page=1`, 5 | requestTrending: `https://api.themoviedb.org/3/movie/popular?api_key=${key}&language=en-US&page=2`, 6 | requestHorror: `https://api.themoviedb.org/3/search/movie?api_key=${key}&language=en-US&query=horror&page=1&include_adult=false`, 7 | requestUpcoming: `https://api.themoviedb.org/3/movie/upcoming?api_key=${key}&language=en-US&page=1`, 8 | }; 9 | 10 | export default requests; -------------------------------------------------------------------------------- /src/components/Main.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React, { useEffect, useState } from 'react'; 3 | import requests from '../Requests'; 4 | 5 | const Main = () => { 6 | const [movies,setMovies] = useState([]); 7 | const movie = movies[Math.floor(Math.random()*movies.length)]; 8 | useEffect(() => { 9 | axios.get(requests.requestPopular).then((response) => { 10 | setMovies(response.data.results) 11 | }) 12 | },[]); 13 | const truncateString = (str,num) => { 14 | if(str?.length >num){ 15 | return str.slice(0,num) + '...'; 16 | }else{ return str;} 17 | } 18 | 19 | return ( 20 |
21 |
22 |
23 | {movie?.title}/ 24 |
25 |

{movie?.title}

26 |
27 | 28 | 29 |
30 |

Released: {movie?.release_date}

31 |

{truncateString(movie?.overview,200)}

32 |
33 |
34 |
35 | ) 36 | }; 37 | 38 | export default Main; 39 | -------------------------------------------------------------------------------- /src/components/Movie.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { FaHeart, FaRegHeart } from 'react-icons/fa'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | import { db } from '../firebase'; 5 | import { arrayUnion, doc, updateDoc } from 'firebase/firestore'; 6 | 7 | const Movie = ({ item }) => { 8 | const [like, setLike] = useState(false); 9 | const [saved, setSaved] = useState(false); 10 | const {user} = UserAuth(); 11 | const movieId = doc(db,'users',`${user?.email}`) 12 | 13 | const saveShow = async () => { 14 | if(user?.email){ 15 | setLike(!like); 16 | setSaved(true); 17 | await updateDoc(movieId, { 18 | savedShows: arrayUnion({ 19 | id:item.id, 20 | title:item.title, 21 | img: item.backdrop_path, 22 | }), 23 | }); 24 | }else{ 25 | alert('Please log in to save a movie'); 26 | } 27 | }; 28 | return ( 29 |
30 | {item?.title} 35 |
36 |

37 | {item?.title} 38 |

39 |

40 | {like ? ( 41 | 42 | ) : ( 43 | 44 | )} 45 |

46 |
47 |
48 | ); 49 | }; 50 | 51 | export default Movie; -------------------------------------------------------------------------------- /src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link, useNavigate} from 'react-router-dom'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | 5 | const Navbar = () => { 6 | const {user,logOut} = UserAuth(); 7 | const navigate = useNavigate(); 8 | const handleLogOut = async () => { 9 | try{ 10 | await logOut(); 11 | navigate('/'); 12 | }catch(error){ 13 | console.log(error); 14 | } 15 | } 16 | return ( 17 |
18 | 19 |

NETFLIX

20 | 21 | {user?.email ? 22 | (
23 | 24 | 25 | 26 | 27 |
) 28 | : 29 | (
30 | 31 | 32 | 33 | 34 | 35 | 36 |
) 37 | } 38 |
39 | ) 40 | }; 41 | 42 | export default Navbar; 43 | -------------------------------------------------------------------------------- /src/components/ProtectedRoute.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate } from 'react-router-dom'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | const ProtectedRoute = ({children}) => { 5 | const {user} = UserAuth() 6 | if(!user){ 7 | return 8 | }else{ 9 | return children; 10 | } 11 | } 12 | 13 | export default ProtectedRoute 14 | -------------------------------------------------------------------------------- /src/components/Row.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React, { useEffect, useState } from 'react'; 3 | import Movie from './Movie'; 4 | import { MdChevronLeft, MdChevronRight } from 'react-icons/md'; 5 | 6 | const Row = ({ title, fetchURL, rowID }) => { 7 | const [movies, setMovies] = useState([]); 8 | 9 | useEffect(() => { 10 | axios.get(fetchURL).then((response) => { 11 | setMovies(response.data.results); 12 | }); 13 | }, [fetchURL]); 14 | 15 | const slideLeft = () => { 16 | var slider = document.getElementById('slider' + rowID); 17 | slider.scrollLeft = slider.scrollLeft - 500; 18 | }; 19 | const slideRight = () => { 20 | var slider = document.getElementById('slider' + rowID); 21 | slider.scrollLeft = slider.scrollLeft + 500; 22 | }; 23 | 24 | return ( 25 | <> 26 |

{title}

27 |
28 | 33 |
37 | {movies.map((item, id) => ( 38 | 39 | ))} 40 |
41 | 46 |
47 | 48 | ); 49 | }; 50 | 51 | export default Row; -------------------------------------------------------------------------------- /src/components/SavedShows.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { MdChevronLeft, MdChevronRight } from 'react-icons/md'; 3 | import { UserAuth } from '../context/AuthContext'; 4 | import { db } from '../firebase'; 5 | import { updateDoc, doc, onSnapshot } from 'firebase/firestore'; 6 | import { AiOutlineClose } from 'react-icons/ai'; 7 | 8 | const SavedShows = () => { 9 | const [movies, setMovies] = useState([]); 10 | const { user } = UserAuth(); 11 | 12 | const slideLeft = () => { 13 | var slider = document.getElementById('slider'); 14 | slider.scrollLeft = slider.scrollLeft - 500; 15 | }; 16 | const slideRight = () => { 17 | var slider = document.getElementById('slider'); 18 | slider.scrollLeft = slider.scrollLeft + 500; 19 | }; 20 | 21 | useEffect(() => { 22 | onSnapshot(doc(db, 'users', `${user?.email}`), (doc) => { 23 | setMovies(doc.data()?.savedShows); 24 | }); 25 | }, [user?.email]); 26 | 27 | const movieRef = doc(db, 'users', `${user?.email}`) 28 | const deleteShow = async (passedID) => { 29 | try { 30 | const result = movies.filter((item) => item.id !== passedID) 31 | await updateDoc(movieRef, { 32 | savedShows: result 33 | }) 34 | } catch (error) { 35 | console.log(error) 36 | } 37 | } 38 | 39 | return ( 40 | <> 41 |

My Shows

42 |
43 | 48 |
52 | {movies.map((item) => ( 53 |
57 | {item?.title} 62 |
63 |

64 | {item?.title} 65 |

66 |

deleteShow(item.id)} className='absolute text-gray-300 top-4 right-4'>

67 |
68 |
69 | ))} 70 |
71 | 76 |
77 | 78 | ); 79 | }; 80 | 81 | export default SavedShows; -------------------------------------------------------------------------------- /src/context/AuthContext.js: -------------------------------------------------------------------------------- 1 | import { createContext,useContext,useEffect,useState } from "react"; 2 | import { auth ,db} from "../firebase"; 3 | import {createUserWithEmailAndPassword,signInWithEmailAndPassword,signOut,onAuthStateChanged} from 'firebase/auth'; 4 | import {setDoc,doc} from 'firebase/firestore'; 5 | 6 | const AuthContext = createContext(); 7 | export function AuthContextProvider({children}){ 8 | const [user,setUser] = useState({}); 9 | function signUp(email,password){ 10 | createUserWithEmailAndPassword(auth,email,password); 11 | setDoc(doc(db,'users',email),{ 12 | savedShows:[] 13 | }) 14 | } 15 | function logIn(email,password){ 16 | return signInWithEmailAndPassword(auth,email,password); 17 | } 18 | function logOut(){ 19 | return signOut(auth); 20 | } 21 | useEffect(() =>{ 22 | const unsubscribe = onAuthStateChanged(auth,(currentUser) => { 23 | setUser(currentUser); 24 | }); 25 | return () => { 26 | unsubscribe(); 27 | }; 28 | }); 29 | return ( 30 | 31 | {children} 32 | 33 | ) 34 | }; 35 | export function UserAuth(){ 36 | return useContext(AuthContext) 37 | }; -------------------------------------------------------------------------------- /src/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import { getAuth } from "firebase/auth"; 4 | import {getFirestore} from 'firebase/firestore'; 5 | // TODO: Add SDKs for Firebase products that you want to use 6 | // https://firebase.google.com/docs/web/setup#available-libraries 7 | 8 | // Your web app's Firebase configuration 9 | const firebaseConfig = { 10 | apiKey: process.env.REACT_APP_FIREBASE_API_KEY, 11 | authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, 12 | projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID, 13 | storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, 14 | messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER, 15 | appId: process.env.REACT_APP_APP_ID 16 | }; 17 | 18 | // Initialize Firebase 19 | export const app = initializeApp(firebaseConfig); 20 | export const auth = getAuth(app); 21 | export const db = getFirestore(app); -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body{ 6 | background-color: #000000; 7 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 8 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /src/pages/Account.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SavedShows from '../components/SavedShows'; 3 | 4 | const Account = () => { 5 | return ( 6 | <> 7 |
8 | / 13 |
14 |
15 |

My Shows

16 |
17 |
18 | 19 | 20 | ); 21 | }; 22 | 23 | export default Account; -------------------------------------------------------------------------------- /src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Main from '../components/Main' 3 | import Row from '../components/Row' 4 | import requests from '../Requests' 5 | 6 | const Home = () => { 7 | return ( 8 | <> 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default Home -------------------------------------------------------------------------------- /src/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState } from 'react'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { UserAuth } from '../context/AuthContext'; 5 | const Login = () => { 6 | const [email,setEmail] = useState(''); 7 | const [password,setPassword] = useState(''); 8 | const [error,setError] = useState(''); 9 | const {user,logIn} = UserAuth(); 10 | const navigate = useNavigate(); 11 | 12 | const handleSubmit = async (e) => { 13 | e.preventDefault(); 14 | setError(''); 15 | try{ 16 | await logIn(email,password); 17 | navigate('/'); 18 | } 19 | catch(error){ 20 | console.log(error); 21 | setError(error.message); 22 | } 23 | } 24 | 25 | return ( 26 | <> 27 |
28 | / 29 |
30 |
31 |
32 |
33 |

Sign In

34 | {error ?

{error}

:null} 35 |
36 | setEmail(e.target.value)} className='p-3 my-2 bg-gray-700 rounded' type='email' placeholder='Email' autoComplete='email'> 37 | setPassword(e.target.value)} className='p-3 my-2 bg-gray-700 rounded' type='password' placeholder='Password' autoComplete='current-password'> 38 | 39 |
40 |

Remember me

41 |

Need Help?

42 |
43 |

New to Netflix?{' '}Sign Up

44 |
45 |
46 |
47 |
48 |
49 | 50 | ) 51 | } 52 | 53 | export default Login 54 | -------------------------------------------------------------------------------- /src/pages/Signup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link,useNavigate} from 'react-router-dom'; 3 | import {UserAuth} from '../context/AuthContext'; 4 | import { useState } from 'react'; 5 | const Signup = () => { 6 | const [email,setEmail] = useState(''); 7 | const [password,setPassword] = useState(''); 8 | const {user,signUp} = UserAuth(); 9 | const navigate = useNavigate(); 10 | const handleSubmit = async (e) => { 11 | e.preventDefault(); 12 | try{ 13 | await signUp(email,password); 14 | navigate('/'); 15 | }catch (error){ 16 | console.log(error); 17 | } 18 | } 19 | 20 | return ( 21 | <> 22 |
23 | / 24 |
25 |
26 |
27 |
28 |

Sign Up

29 |
30 | setEmail(e.target.value)} className='p-3 my-2 bg-gray-700 rounded' type='email' placeholder='Email' autoComplete='email'> 31 | setPassword(e.target.value)} className='p-3 my-2 bg-gray-700 rounded' type='password' placeholder='Password' autoComplete='current-password'> 32 | 33 |
34 |

Remember me

35 |

Need Help?

36 |
37 |

Already subscribed to Netflix?{' '}Sign In

38 |
39 |
40 |
41 |
42 |
43 | 44 | ) 45 | } 46 | 47 | export default Signup 48 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require('tailwind-scrollbar-hide')], 8 | } --------------------------------------------------------------------------------