├── 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 |

8 |
15 |

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 | */
--------------------------------------------------------------------------------