├── src ├── Axios.jsx ├── constants │ └── Constants.jsx ├── index.js ├── App.css ├── urls.jsx ├── component │ ├── navbar │ │ ├── NavBar.jsx │ │ └── NavBar.css │ ├── banner │ │ ├── Banner.jsx │ │ └── Banner.css │ └── post │ │ ├── post.css │ │ └── Post.jsx └── App.js ├── README.md ├── .gitignore ├── public ├── index.html └── manifest.json ├── package.json └── license /src/Axios.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import {baseUrl} from "./constants/Constants.jsx" 3 | 4 | const instance = axios.create({ 5 | baseURL: baseUrl 6 | }); 7 | export default instance; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React.js + API 2 | 3 | To See Project{ 4 | 5 | git clone https://github.com/callmesidhu/react-netflix-clone.git 6 | npm install 7 | npm start 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/constants/Constants.jsx: -------------------------------------------------------------------------------- 1 | export const baseUrl='https://api.themoviedb.org/3'; 2 | export const API_KEY="2365b4f588ff705929c3e8c3ce1c5e72"; 3 | export const imageUrl = "https://image.tmdb.org/t/p/original" 4 | 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | 5 | } 6 | body{ 7 | background-color: #111; 8 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 9 | } 10 | body::-webkit-scrollbar{ 11 | display: none; 12 | } 13 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | Netflix 13 | 14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /src/urls.jsx: -------------------------------------------------------------------------------- 1 | import { API_KEY } from "./constants/Constants"; 2 | 3 | export const originals=`discover/tv?api_key=${API_KEY}&with_networks=213` 4 | export const trending=`trending/all/week?api_key=${API_KEY}&language=en-US` 5 | export const action=`discover/movie?api_key=${API_KEY}&with_genres=28` 6 | export const comedy=`discover/movie?api_key=${API_KEY}&with_genres=35` 7 | export const horror=`discover/movie?api_key=${API_KEY}&with_genres=27` 8 | export const romantic=`discover/movie?api_key=${API_KEY}&with_genres=10749` 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/component/navbar/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import "./NavBar.css"; 3 | 4 | function NavBar() { 5 | return ( 6 |
7 | Netflix 8 |
9 | Home 10 | TV Shows 11 | Movies 12 | Originals 13 | Recently Added 14 |
15 | avatar 16 | 17 |
18 | 19 | ) 20 | } 21 | 22 | export default NavBar; 23 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import {originals,action, trending, comedy, horror, romantic} from './urls' 2 | import NavBar from './component/navbar/NavBar.jsx' 3 | import Banner from './component/banner/Banner.jsx' 4 | import Post from './component/post/Post' 5 | import React from 'react' 6 | import './App.css' 7 | 8 | function App() { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | ) 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /src/component/navbar/NavBar.css: -------------------------------------------------------------------------------- 1 | 2 | .navbar{ 3 | background-color: #111; 4 | z-index: 1; 5 | position: fixed; 6 | top: 0; 7 | width: 100%; 8 | height: 40px; 9 | padding: 20px; 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | .netflix-logo{ 14 | position: fixed; 15 | top: -10px; 16 | width: 110px; 17 | left: 20px; 18 | } 19 | .avatar{ 20 | position: fixed; 21 | right: 20px; 22 | width: 30px; 23 | margin-right: 20px; 24 | z-index: 1; 25 | } 26 | a { 27 | color: white; 28 | text-decoration: none; 29 | margin-left: 100px; 30 | margin: 5px; 31 | } 32 | .home-bar{ 33 | margin-left: 15%; 34 | margin-top: 10px; 35 | } 36 | a:hover{ 37 | color:gray 38 | } 39 | -------------------------------------------------------------------------------- /src/component/banner/Banner.jsx: -------------------------------------------------------------------------------- 1 | import {imageUrl} from "../../constants/Constants.jsx"; 2 | import {useEffect, useState} from "react"; 3 | import axios from '../../Axios.jsx' 4 | import "./Banner.css"; 5 | 6 | 7 | 8 | function Banner(props) { 9 | const [movie, setMovie] = useState() 10 | useEffect(() => { 11 | axios.get(props.url).then((response)=>{ 12 | setMovie(response.data.results[1]) 13 | console.log(response.data) 14 | 15 | }) 16 | }, [props.url]) 17 | 18 | 19 | return ( 20 |
21 | 22 |
23 | 24 |

25 | {movie?movie.name:""} 26 |

27 |
28 | 29 | 30 |
31 |

{movie ? movie.overview : ''}

32 |
33 |
34 |
35 | 36 | ) 37 | } 38 | 39 | export default Banner; 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://callmesidhu-netflix.netlify.app/", 3 | "name": "try", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@flaticon/flaticon-uicons": "^2.0.1", 8 | "@testing-library/jest-dom": "^5.16.5", 9 | "@testing-library/react": "^13.4.0", 10 | "@testing-library/user-event": "^13.5.0", 11 | "axios": "^1.3.4", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-scripts": "5.0.1", 15 | "react-youtube": "^10.1.0", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "predeploy": "npm run build", 20 | "deploy": "gh-pages -d build", 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 | "gh-pages": "^6.1.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/component/post/post.css: -------------------------------------------------------------------------------- 1 | .row{ 2 | margin-left: 20px; 3 | color: white; 4 | } 5 | .posters{ 6 | display:flex; 7 | padding:20px; 8 | overflow-x: auto; 9 | overflow-y: hidden; 10 | white-space: nowrap; 11 | 12 | 13 | } 14 | 15 | .poster{ 16 | margin-top: 15px; 17 | max-height: 250px; 18 | margin-right: 15px; 19 | cursor: pointer; 20 | scroll-behavior: smooth; 21 | animation: scroll 360s linear infinite; 22 | } 23 | .poster:hover{ 24 | transform: scale(1.2); 25 | transition-duration: 0.5s; 26 | } 27 | @keyframes scroll { 28 | 0% { 29 | transform: translateX(0); 30 | } 31 | 100% { 32 | transform: translateX(-1600%); 33 | } } 34 | .posters::-webkit-scrollbar{ 35 | display: none; 36 | } 37 | 38 | .small-poster{ 39 | max-height: 150px; 40 | cursor: pointer; 41 | margin-right: 15px; 42 | } 43 | 44 | 45 | .small-poster:hover{ 46 | transform: scale(1.3); 47 | transition-duration: 0.5s; 48 | 49 | } 50 | 51 | .sub-nav { 52 | grid-area: sb; 53 | padding: 0 40px 0 40px; 54 | } 55 | 56 | .sub-nav a { 57 | color: var(--light); 58 | text-decoration: none; 59 | margin: 5px; 60 | } 61 | 62 | .sub-nav a:hover { 63 | color: var(--dark); 64 | } 65 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, S Sidharth 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/component/banner/Banner.css: -------------------------------------------------------------------------------- 1 | .banner{ 2 | background-size: cover; 3 | height: 600px; 4 | color: white; 5 | } 6 | .content{ 7 | padding-top: 140px; 8 | height: 190px; 9 | padding-left: 30px; 10 | } 11 | .title{ 12 | font-size: 3rem; 13 | font-weight: 800; 14 | padding-bottom: 0.3rem; 15 | text-shadow: 1px 1px 30px #111; 16 | } 17 | .button{ 18 | color: white; 19 | width: 111px; 20 | border:none; 21 | outline: none; 22 | font-weight: 800; 23 | border-radius: 5px; 24 | padding-left: 2rem; 25 | padding-right: 2rem; 26 | padding-top: 0.5rem; 27 | padding-bottom: 0.5rem; 28 | background-color: rgba(51,51,51,0.5); 29 | cursor: pointer; 30 | margin-right: 2rem; 31 | display: flex; 32 | display:inline-block; 33 | text-align: center; 34 | white-space: nowrap; 35 | 36 | } 37 | .button:hover{ 38 | color: black; 39 | background-color: #e6e6e6; 40 | } 41 | 42 | .description{ 43 | width: 45rem; 44 | line-height: 1.3; 45 | padding-top: 1rem; 46 | font-size: 1rem; 47 | height: 80px; 48 | max-width: 500px; 49 | text-shadow: 1px 1px 7px #111; 50 | 51 | } 52 | .fade-bottom{ 53 | height: 16.9rem; 54 | background-image: linear-gradient(to bottom , rgba(255,0,0,0), rgba(17, 17, 17, 1) 55 | ); 56 | } -------------------------------------------------------------------------------- /src/component/post/Post.jsx: -------------------------------------------------------------------------------- 1 | import { API_KEY } from '../../constants/Constants.jsx'; 2 | import {imageUrl} from '../../constants/Constants.jsx' 3 | import React, { useEffect, useState } from 'react' 4 | import YouTube from 'react-youtube'; 5 | import axios from '../../Axios.jsx' 6 | import './post.css'; 7 | 8 | 9 | 10 | 11 | function Post(props) { 12 | const [movies, setMovies] = useState([]) 13 | const [urlId, setUrlId] = useState() 14 | useEffect(() => { 15 | axios.get(props.url).then(response=>{ 16 | console.log(response.data) 17 | setMovies(response.data.results) 18 | }).catch(err=>{ 19 | alert('Please verify the code') 20 | }) 21 | }, [props.url]) 22 | 23 | 24 | const movieTrailerId =(id)=>{ 25 | axios.get(`/movie/${id}/videos?api_key=${API_KEY}&language=en-US`).then(response=>{ 26 | console.log(response.data) 27 | if(response.data.results.lenght!==0){ 28 | setUrlId(response.data.results[0]) 29 | }else{ 30 | console.log('empty Array') 31 | } 32 | })} 33 | 34 | const opts = { 35 | height: '400', 36 | width: '100%', 37 | playerVars: {autoplay: 1} 38 | }; 39 | return ( 40 | 41 | 42 | 43 |
44 |

{props.title}

45 |
46 | {movies.map((obj)=> 47 | movieTrailerId(obj.id)} className={props.isSmall ? 'small-poster' : 'poster'} src={`${imageUrl+obj.backdrop_path}`} alt=''> 48 | )} 49 | 50 |
51 | 52 |
{urlId && }
53 |
54 | ) 55 | 56 | } 57 | 58 | export default Post; 59 | 60 | 61 | /* 62 | <---drag and scroll in x-direction(for adding more feature)----> 63 | 64 | 65 | 66 | 67 | const posters = document.querySelector('.posters'); 68 | 69 | let isDown = false; 70 | let startX, scrollLeft; 71 | 72 | content.addEventListener('mousedown', (e) => { 73 | isDown = true; 74 | startX = e.pageX - content.offsetLeft; 75 | scrollLeft = content.scrollLeft; 76 | }); 77 | 78 | content.addEventListener('mouseleave', () => { 79 | isDown = false; 80 | }); 81 | 82 | content.addEventListener('mouseup', () => { 83 | isDown = false; 84 | }); 85 | 86 | content.addEventListener('mousemove', (e) => { 87 | if (!isDown) return; 88 | e.preventDefault(); 89 | const x = e.pageX - content.offsetLeft; 90 | const walk = (x - startX) * 1.5; 91 | content.scrollLeft = scrollLeft - walk; 92 | }); 93 | */ --------------------------------------------------------------------------------