├── .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 | You need to enable JavaScript to run this app.
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 |
24 |
25 |
{movie?.title}
26 |
27 | Play
28 | Watch Later
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 |
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 | Account
25 |
26 | Logout
27 |
)
28 | :
29 | (
30 |
31 | Sign In
32 |
33 |
34 | Sign Up
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 |
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 |
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 |
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 | }
--------------------------------------------------------------------------------