Skip to content
satish-kumar75Public

About

A simple movie website where you can explore different types of movies and tv shows and also watch it live.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

25 Commits

Folders and files

Repository files navigation


Project Banner
react.js SASS redux toolkit vite

Movix

  1. πŸ€– Introduction
  2. βš™οΈ Tech Stack
  3. πŸ”‹ Features
  4. 🀸 Quick Start
  5. πŸ•ΈοΈ Snippets
  6. πŸ”— Links

I developed a stunning, fully responsive movie website using the TMDB API and React. It features a dynamic Home page for popular and trending movies, detailed movie pages with trailers and cast info, and an Explore section with infinite scrolling.

  • React.js
  • Redux Toolkit
  • Vite
  • SASS

πŸ‘‰ Dynamic Home Page: Showcases popular and trending movies with lazy-loaded images and interactive carousels.

πŸ‘‰ Detailed Movie Pages: Includes comprehensive information, cast details, trailers, and video clips using a React video player.

πŸ‘‰ Cast Biographies and Filmographies: Users can explore detailed biographies and filmographies by clicking on cast images.

πŸ‘‰ Explore Section: Allows users to discover movies and TV series with enhanced infinite scrolling using react-infinite-scroll-component.

πŸ‘‰ Completely Responsive: Ensures consistent access and optimal viewing on any device with a fully responsive design.

πŸ‘‰ Global State Management with Redux: Efficient state management across the entire application.

πŸ‘‰ Where to Watch: Provider logos under the rating row show where a title streams, rents or sells in the visitor's country, with a country picker and a free legal download for public-domain classics from the Internet Archive.

πŸ‘‰ Languages and Seasons: Language and age rating on each title, trailers filtered by language, an Explore filter for Hindi, Tamil, Telugu and more, and a season and episode browser for TV shows.

πŸ‘‰ My List: Save titles for later, stored only in the visitor's browser.

πŸ‘‰ Works on Blocked Networks: TMDB data and images are served through a same-origin proxy on Vercel, so the site loads even where TMDB is blocked and the API token never reaches the browser.

πŸ‘‰ Search-Friendly: Per-page titles, descriptions and Open Graph tags, server-rendered metadata and Movie/TVSeries structured data for title pages, a dynamic sitemap and robots.txt.

πŸ‘‰ About and Privacy Pages: Plain-language pages describing the project and exactly what data the site touches, plus an animated 404 page.

Follow these steps to set up the project locally on your machine.

Prerequisites

Make sure you have the following installed on your machine:

Cloning the Repository

git clone https://github.com/satish-kumar75/Movix.git
cd Movix

Installation

Install the project dependencies using npm:

npm install

Environment Variables

Copy .env.example to .env and fill in your values:

VITE_APP_TMDB_TOKEN=your TMDB API Read Access Token
VITE_APP_VIDSRC_URL=base URL of the embedded player
PROXY_TARGET=https://your-app.vercel.app

VITE_APP_TMDB_TOKEN is only read by the serverless functions in api/, never by the browser bundle. PROXY_TARGET is used by the Vite dev server to forward /api and /tmdb-img to your deployed site, so local development works even on networks that block TMDB. Put it in .env.local if you prefer. .env files are git-ignored.

Running the Project

npm run dev

Open http://localhost:5173 in your browser to view the project.

Deploying to Vercel

Import the repository, add VITE_APP_TMDB_TOKEN and VITE_APP_VIDSRC_URL under Project Settings β†’ Environment Variables, and deploy. vercel.json already contains the proxy rewrites, the SPA fallback and the security headers, and package.json pins Node 24.

index.scss
:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 1;
  font-weight: 500;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;

  --black: #04152d;
  --black2: #041226;
  --black3: #020c1b;
  --black-lighter: #1c4b91;
  --black-light: #173d77;
  --pink: #da2f68;
  --orange: #f89e00;
  --gradient: linear-gradient(98.37deg, #f89e00 0.99%, #da2f68 100%);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background-color: var(--black);
}

::-webkit-scrollbar {
  display: none;
}

.skeleton {
  position: relative;
  overflow: hidden;
  background-color: #0a2955;
  &::after {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    transform: translateX(-100%);
    background-image: linear-gradient(
      90deg,
      rgba(#193763, 0) 0,
      rgba(#193763, 0.2) 20%,
      rgba(#193763, 0.5) 60%,
      rgba(#193763, 0)
    );
    animation: shimmer 2s infinite;
    content: "";
  }

  @keyframes shimmer {
    100% {
      transform: translateX(100%);
    }
  }
}
Img.jsx
import React from "react";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";

const Img = ({ src, className }) => {
  return (
    <LazyLoadImage className={className || ""} alt="" effect="blur" src={src} />
  );
};

export default Img;
mixins.scss
@mixin sm {
  @media only screen and (min-width: 640px) {
    @content;
  }
}

@mixin md {
  @media only screen and (min-width: 768px) {
    @content;
  }
}

@mixin lg {
  @media only screen and (min-width: 1024px) {
    @content;
  }
}

@mixin xl {
  @media only screen and (min-width: 1280px) {
    @content;
  }
}

@mixin xxl {
  @media only screen and (min-width: 1536px) {
    @content;
  }
}

@mixin ellipsis($line: 2) {
  display: -webkit-box;
  -webkit-line-clamp: $line;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}
useFetch.jsx
import { useEffect, useState } from "react";
import { fetchDataFromApi } from "../utils/api";
const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading("loading...");
    setData(null);
    setError(null);

    fetchDataFromApi(url)
      .then((res) => {
        setLoading(false);
        setData(res);
      })
      .catch((err) => {
        setLoading(false);
        setError("Something went wrong!");
      });
  }, [url]);

  return { data, loading, error };
};

export default useFetch;
package.json
"dependencies": {
    "@reduxjs/toolkit": "^1.9.1",
    "axios": "^1.2.2",
    "dayjs": "^1.11.7",
    "react": "^18.2.0",
    "react-circular-progressbar": "^2.1.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.7.1",
    "react-infinite-scroll-component": "^6.1.0",
    "react-lazy-load-image-component": "^1.5.6",
    "react-player": "^2.11.0",
    "react-redux": "^8.0.5",
    "react-router-dom": "^6.6.2",
    "react-select": "^5.7.0",
    "sass": "^1.57.1"
  }
PlayIcon.jsx
export const PlayIcon = () => {
  return (
    <svg
      version="1.1"
      xmlns="http://www.w3.org/2000/svg"
      xmlnsXlink="http://www.w3.org/1999/xlink"
      x="0px"
      y="0px"
      width="80px"
      height="80px"
      viewBox="0 0 213.7 213.7"
      enableBackground="new 0 0 213.7 213.7"
      xmlSpace="preserve"
    >
      <polygon
        className="triangle"
        fill="none"
        strokeWidth="7"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeMiterlimit="10"
        points="73.5,62.5 148.5,105.8 73.5,149.1 "
      ></polygon>
      <circle
        className="circle"
        fill="none"
        strokeWidth="7"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeMiterlimit="10"
        cx="106.8"
        cy="106.8"
        r="103.3"
      ></circle>
    </svg>
  );
};
Spinner.jsx
import React from "react";

import "./style.scss";

const Spinner = ({ initial }) => {
    return (
        <div className={`loadingSpinner ${initial ? "initial" : ""}`}>
            <svg className="spinner" viewBox="0 0 50 50">
                <circle
                    className="path"
                    cx="25"
                    cy="25"
                    r="20"
                    fill="none"
                    strokeWidth="5"
                ></circle>
            </svg>
        </div>
    );
};

export default Spinner;

// CSS
.loadingSpinner {
    width: 100%;
    height: 150px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    .spinner {
        animation: rotate 2s linear infinite;
        z-index: 2;
        width: 50px;
        height: 50px;
        & .path {
            stroke: hsl(210, 70, 75);
            stroke-linecap: round;
            animation: dash 1.5s ease-in-out infinite;
        }
    }

    &.initial {
        height: 700px;
    }

    @keyframes rotate {
        100% {
            transform: rotate(360deg);
        }
    }

    @keyframes dash {
        0% {
            stroke-dasharray: 1, 150;
            stroke-dashoffset: 0;
        }
        50% {
            stroke-dasharray: 90, 150;
            stroke-dashoffset: -35;
        }
        100% {
            stroke-dasharray: 90, 150;
            stroke-dashoffset: -124;
        }
    }
}

Public Assets used in the project can be found here

About

A simple movie website where you can explore different types of movies and tv shows and also watch it live.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages