Building a Movie Finder Website using React
Introduction
In this blog, we will walk through the process of building a Movie Finder website using React and the OMDB API. The website allows users to browse movies by categories like Avengers, Star Wars, and Series, and search for movies using specific queries. Each movie has its detail page, making it easy to explore more about your favorite films.
Project Overview
Movie Finder Website enables users to:
- Browse categories like Avengers and Star Wars.
- Search for movies by keywords.
- View detailed movie information (poster, genre, director, actors, and more).
- Navigate easily through the website with a clean, modern design.
Features
- Fetch data dynamically using the OMDB API.
- Responsive design for a better user experience.
- Search functionality that provides instant results.
- Loading indicators while data is being fetched.
- View details of individual movies on a separate page.
Technologies Used
- React: Frontend library for building UI components.
- React Router: For navigation and routing.
- Axios: For making HTTP requests to the OMDB API.
- OMDB API: To fetch movie details.
- CSS: For styling the application.
Project Structure
Here is the directory structure for the project:
movie-finder/ ├── public/ ├── src/ │ ├── components/ │ │ └── Navbar.js │ │ └── Footer.js │ ├── pages/ │ │ └── Home.js │ │ └── Movies.js │ │ └── Series.js │ │ └── SearchResults.js │ │ └── MovieDetail.js │ └── App.js │ └── App.css └── package.json
Installation
-
Clone the repository:
git clone https://github.com/abhishekgurjar-in/movie-finder.git cd movie-finder
Copy after login -
Install dependencies:
npm install
Copy after login Get your API key from OMDB API.
-
Create a .env file in the project root and add your API key:
REACT_APP_OMDB_API_KEY=yourapikey
Copy after login -
Run the project:
npm start
Copy after login
Usage
1. Home Page
The Home Page showcases two categories of movies: Avengers and Star Wars. When the user clicks on a movie card, they are redirected to the detailed movie page.
Code Snippet from Home.js:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; import Movies from "./Movies"; import Series from "./Series"; const Home = () => { const [avengersMovies, setAvengersMovies] = useState([]); const [starWarsMovies, setStarWarsMovies] = useState([]); const [loadingAvengers, setLoadingAvengers] = useState(true); const [loadingStarWars, setLoadingStarWars] = useState(true); const navigate = useNavigate(); useEffect(() => { fetchMovies("Avengers", setAvengersMovies, setLoadingAvengers); fetchMovies("Star Wars", setStarWarsMovies, setLoadingStarWars); }, []); const fetchMovies = (query, setMovies, setLoading) => { axios .get(`http://www.omdbapi.com/?s=${query}&apikey=you_api_key`) .then((response) => { if (response.data.Search) { setMovies(response.data.Search); } else { setMovies([]); // Clear movies if no results } }) .catch((error) => { console.error(`There was an error fetching the ${query} movies!`, error); setMovies([]); // Clear movies if there is an error }) .finally(() => { setLoading(false); }); }; const handleCardClick = (id) => { navigate(`/movie/${id}`); }; const renderMovies = (movies, loading) => ( loading ? ( <div className="loader"><div className="load"></div></div> ) : ( <div className="cards"> {movies.length > 0 ? ( movies.map((movie) => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h2>{movie.Title}</h2> </div> )) ) : ( <p>No movies found.</p> )} </div> ) ); return ( <> <div className="home"> <div className="movie-category"> <h4>Avengers Movies</h4> {renderMovies(avengersMovies, loadingAvengers)} </div> <br /> <br /> <div className="movie-category"> <h4>Star Wars Movies</h4> {renderMovies(starWarsMovies, loadingStarWars)} </div> </div> <Movies /> <Series /> </> ); }; export default Home;
2. Search Functionality
The user can search for any movie using the search bar at the top of the website. Search results are fetched from the OMDB API based on the user’s query.
Code Snippet from SearchResults.js:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useParams, useNavigate } from "react-router-dom"; const SearchResults = () => { const [movies, setMovies] = useState([]); const [loading, setLoading] = useState(false); const { query } = useParams(); const navigate = useNavigate(); // Add this line to use navigate useEffect(() => { if (query) { setLoading(true); // Set loading to true before starting the fetch axios .get(`http://www.omdbapi.com/?s=${query}&apikey=your_api_key`) .then((response) => { if (response.data.Search) { setMovies(response.data.Search); } else { setMovies([]); // Clear movies if no results } }) .catch((error) => { console.error("There was an error fetching the movie data!", error); }) .finally(() => { setLoading(false); // Set loading to false once fetch is complete }); } }, [query]); const handleCardClick = (id) => { navigate(`/movie/${id}`); // Navigate to movie detail page }; return ( <div className="search-results"> <h4>Search Results for "{query}"</h4> {loading ? ( <div className="loader"><div className="load"></div></div> // Loader ) : ( <div className="cards"> {movies.length > 0 ? ( movies.map((movie) => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h2>{movie.Title}</h2> </div> )) ) : ( <p>No results found.</p> )} </div> )} </div> ); }; export default SearchResults;
3. Movie Detail Page
When a user clicks on a movie, they are redirected to the movie detail page. This page shows the movie’s title, poster, plot, actors, and more.
Code Snippet from MovieDetail.js:
import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { useParams } from 'react-router-dom'; const MovieDetail = () => { const [movie, setMovie] = useState(null); const [loading, setLoading] = useState(true); const { id } = useParams(); useEffect(() => { axios.get(`http://www.omdbapi.com/?i=${id}&apikey=your_api_key`) .then((response) => { setMovie(response.data); }) .catch((error) => { console.error("There was an error fetching the movie details!", error); }) .finally(() => { setLoading(false); }); }, [id]); if (loading) return <div className="loader"> <div className="load"></div> </div>; if (!movie) return <div className='loader'>No movie data found!</div>; return ( <div className="movie-detail"> <div className="detail-box"> <h1>{movie.Title}</h1> <p><strong>Year:</strong> {movie.Year}</p> <p><strong>Rating:</strong> {movie.imdbRating}</p> <p><strong>Genre:</strong> {movie.Genre}</p> <p><strong>Director:</strong> {movie.Director}</p> <p><strong>Actors:</strong> {movie.Actors}</p> <p><strong>Plot:</strong> {movie.Plot}</p> <p><strong>Runtime:</strong> {movie.Runtime}</p> <p><strong>Language:</strong> {movie.Language}</p> <p><strong>Country:</strong> {movie.Country}</p> <p><strong>Awards:</strong> {movie.Awards}</p> </div> <div className="img-box"> <img src={movie.Poster} alt={movie.Title} /> </div> </div> ); }; export default MovieDetail;
4. Movies and Series Pages
Movies.js and Series.js pages list movies and TV series, respectively.
Code Snippet from Movies.js:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; const Movies = () => { const [movies, setMovies] = useState([]); const navigate = useNavigate(); useEffect(() => { axios .get(`http://www.omdbapi.com/?s=Avengers&type=movie&apikey=${process.env.REACT_APP_OMDB_API_KEY}`) .then(response => setMovies(response.data.Search || [])) .catch(error => console.error(error)); }, []); const handleCardClick = (id) => { navigate(`/detail/${id}`); }; return ( <div className="movies"> <h2>Movies</h2> <div className="cards"> {movies.map(movie => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h3>{movie.Title}</h3> </div> ))} </div> </div> ); }; export default Movies;
Code Snippet from Series.js:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; const Series = () => { const [series, setSeries] = useState([]); const navigate = useNavigate(); useEffect(() => { axios .get(`http://www.omdbapi.com/?s=Star Wars&type=series&apikey=${process.env.REACT_APP_OMDB_API_KEY}`) .then(response => setSeries(response.data.Search || [])) .catch(error => console.error(error)); }, []); const handleCardClick = (id) => { navigate(`/detail/${id}`); }; return ( <div className="series"> <h2>TV Series</h2> <div className="cards"> {series.map(show => ( <div key={show.imdbID} className="card" onClick={() => handleCardClick(show.imdbID)}> <img src={show.Poster} alt={show.Title} /> <h3>{show.Title}</h3> </div> ))} </div> </div> ); }; export default Series;
5. Navbar Component
The Navbar component allows users to navigate between different pages and perform a search.
Updated Navbar.js
import React, { useState } from "react"; import { NavLink, Link } from "react-router-dom"; const Navbar = () => { const [searchQuery, setSearchQuery] = useState(""); const handleSearch = (event) => { if (event.key === 'Enter' && searchQuery.trim()) { document.getElementById('search-link').click(); } }; return ( <div className="navbar"> <div className="logo"> <h1>Movie Finder</h1> </div> <div className="page-list"> <NavLink to="/"> <h4>Home</h4> </NavLink> <NavLink to="/movies"> <h4>Movies</h4> </NavLink> <NavLink to="/series"> <h4>TV Series</h4> </NavLink> </div> <div className="search-box"> <input type="text" placeholder="Search for movies or series" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={handleSearch} /> <Link to={`/search/${searchQuery}`} id="search-link"> <button>Search</button> </Link> </div> </div> ); }; export default Navbar;
6. Footer Component
The Footer component provides a simple footer message.
Footer.js
import React from 'react'; const Footer = () => { return ( <div className='footer'> Made with <span>❤️</span> by Abhishek Gurjar </div> ); }; export default Footer;
*{ box-sizing: border-box; } body{ font-family: sans-serif; margin: 0; padding: 0; } .navbar { padding-inline: 100px; display: flex; align-items: center; justify-content: space-between; background-color: red; } .search-btn{ background-color: red; } .logo h1{ font-size: 25px; color:black; } .page-list { display: flex; align-items: center; gap: 40px; } .page-list a{ color: white; text-decoration: none; font-size: 20px; } .page-list a:hover{ color: black; } .page-list a.active{ color: black; } .search-box{ box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; background-color:white; color: gray; width: 250px; height: 40px; border-radius: 50px; overflow: hidden; } .search-box input{ width: 200px; height: 40px; margin-left: 10px; border: none; outline: none; } .home{ margin-block: 40px; margin-inline: 60px; } .home h4{ font-size: 16px; } .movies{ margin-block: 40px; margin-inline: 60px; } .movies h4{ font-size: 16px; } .cards{ display: flex; flex-wrap: wrap; align-items:center ; justify-content: space-between; gap: 10px; } .card{ width:200px; height:360px; border-radius: 10px; overflow: hidden; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; } .card img{ width: 200px; height: 290px; object-fit: cover; } .card h2{ margin: 10px; font-size: 16px; text-align: center; } .series{ margin-block: 40px; margin-inline: 60px; } .series h4{ font-size: 16px; } .home{ margin-block: 40px; margin-inline: 60px; } .search-results{ margin-block: 40px; margin-inline: 60px; } .search-results h4{ font-size: 16px; } .loader{ min-height: 90vh; display: flex; align-items: center; justify-content: center; } /* HTML: <div class="loader"></div> */ .load { width: 50px; padding: 8px; aspect-ratio: 1; border-radius: 50%; background: #ff1900; --_m: conic-gradient(#0000 10%,#000), linear-gradient(#000 0 0) content-box; -webkit-mask: var(--_m); mask: var(--_m); -webkit-mask-composite: source-out; mask-composite: subtract; animation: l3 1s infinite linear; } @keyframes l3 {to{transform: rotate(1turn)}} .movie-detail { margin-block: 40px; margin-inline: 60px; display: flex; align-items: flex-start; justify-content: space-between; } img-box{ width: 50%; } .movie-detail img { border-radius: 10px; width: 330px; height: auto; object-fit: cover; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; } .detail-box{ width: 50%; } .movie-detail p { font-size: 18px; margin: 10px 0; } .movie-detail a { display: inline-block; margin-top: 20px; color: #007bff; text-decoration: none; } .movie-detail a:hover { text-decoration: underline; } .footer{ width: 100%; background-color: red; text-align: center; color: white; padding: 20px; }
Live Demo
You can check out the live demo of the Movie Finder website here.
Conclusion
In this blog, we learned how to create a Movie Finder website using React, React Router, and Axios. This project demonstrates how to interact with a public API, manage state in React, and create a simple yet functional movie browsing experience.
Feel free to customize the design and add more features, like user reviews or movie ratings, to make it more dynamic!
Credits
- OMDB API
- React
- React Router
Author
Abhishek Gurjar is a dedicated web developer passionate about creating practical and functional web applications. Check out more of his projects on GitHub.
The above is the detailed content of Building a Movie Finder Website using React. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

It's out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

With the recent climb of Bitcoin’s price over 20k $USD, and to it recently breaking 30k, I thought it’s worth taking a deep dive back into creating Ethereum

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That's like this.

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

I'd say "website" fits better than "mobile app" but I like this framing from Max Lynch:

If we need to show documentation to the user directly in the WordPress editor, what is the best way to do it?

There are a number of these desktop apps where the goal is showing your site at different dimensions all at the same time. So you can, for example, be writing

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...
