Learn how to create a movie search app using HTML, CSS, and JavaScript. Follow this simple step-by-step guide to build your app with API integration.
Table of Contents
Are you interested in creating a simple movie search app? This tutorial will guide you through building a fully functional app using HTML, CSS, and JavaScript. You’ll also learn how to integrate an API to fetch real-time movie data.
This project is perfect for beginners and intermediate developers looking to improve their JavaScript skills. By the end of this guide, you’ll have a working app that can search movies and display their details.
Prerequisites
Before starting, make sure you have:
- Basic knowledge of HTML, CSS, and JavaScript.
- A code editor like VS Code.
- An API key from OMDb API.
Source Code
Step 1 (HTML Code):
To get started, we will first need to create a basic HTML file. In this file, we will include the main structure for our movie search app. Below is a breakdown of each part:
1. Document Declaration and Head Section
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Movie Search App</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
</head>
<!DOCTYPE html>
: Declares the document as an HTML5 document.<html lang="en">
: Specifies the language of the document as English.- Meta Tags:
<meta charset="UTF-8">
: Sets the character encoding to UTF-8 for supporting a wide range of characters.<meta name="viewport" content="width=device-width, initial-scale=1.0">
: Ensures the layout is responsive and scales correctly on different devices.
- Title: Sets the page title to "Movie Search App."
- Fonts: Loads the "Poppins" font from Google Fonts.
- CSS:
- Loads Bootstrap CSS for responsive styling and pre-built components.
- Links to a custom stylesheet (
styles.css
) for additional styling.
2. Body Section
Container and Search Input
<div class="container py-5">
<h1 class="text-center mb-4">🎬 Movie Search App</h1>
<div class="d-flex justify-content-center mb-4">
<input type="text" id="search-input" class="form-control w-50 me-2" placeholder="Enter movie title...">
<button id="search-button" class="btn btn-highlight">Search</button>
</div>
- Container:
- The
container
class centers the content and adds padding (py-5
for vertical padding).
- The
- Heading:
- Displays the app title, styled with
text-center
(center-aligned) andmb-4
(margin-bottom).
- Displays the app title, styled with
- Search Bar:
- Input (
<input>
): A text box (id="search-input"
) for entering movie titles, styled withform-control
(Bootstrap class) andw-50
(width 50%). - Button (
<button>
): A button (id="search-button"
) to trigger the search.
- Input (
Loading Spinner
<div id="loading" class="text-center my-3" style="display: none;">
<div class="spinner-border text-highlight" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
id="loading"
: A hidden (style="display: none;"
) container for a loading spinner.- Spinner:
- The
spinner-border
class creates a circular loading animation. text-highlight
: A custom class to style the spinner color.
- The
Movie Results Container
<div id="movie-container" class="row g-4"></div>
id="movie-container"
: A container for displaying movie cards dynamically.row
: Bootstrap's row class for a grid layout.g-4
: Adds a gap of 4 between grid items.
3. Movie Details Modal
<div class="modal fade" id="movieModal" tabindex="-1" aria-labelledby="movieModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="movieModalLabel">Movie Details</h5>
<button type="button" class="btn-close bg-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="movie-details"></div>
</div>
</div>
</div>
</div>
- Modal Container:
id="movieModal"
: Identifies the modal, controlled by Bootstrap's modal plugin.fade
: Adds a fade-in animation.
- Modal Dialog:
modal-dialog modal-lg
: Defines the size (modal-lg
) of the modal.
- Modal Content:
- Header: Displays the modal title and a close button (
btn-close
). - Body: Contains a placeholder (
id="movie-details"
) for dynamically inserted movie details.
- Header: Displays the modal title and a close button (
4. Scripts
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="script.js"></script>
- Bootstrap JS: Loads Bootstrap's JavaScript functionality (e.g., modal behavior).
- Custom Script: Links to
script.js
, where the app's JavaScript logic resides.
Step 2 (CSS Code):
Once the basic HTML structure of the movie search app is in place, the next step is to add styling to the app using CSS. Here's a breakdown of the key styles:
1. General Body Styling
body {
background-color: #0E0F12;
color: #fff;
font-family: 'Poppins', sans-serif;
}
background-color: #0E0F12
: Sets a dark background for the entire page.color: #fff
: Makes the default text color white for readability.font-family: 'Poppins', sans-serif;
: Applies the Poppins font for a clean, modern look.
2. Heading (<h1>
)
h1 {
color: #91F726;
}
color: #91F726
: Sets the heading text color to the bright green accent.
3. Search Input (#search-input
)
#search-input {
background-color: #1A1A1D;
color: #fff;
border: 1px solid #91F726;
}
#search-input:focus {
box-shadow: none;
}
#search-input::placeholder {
color: #777;
}
background-color: #1A1A1D
: Gives the input field a dark background.color: #fff
: Sets the text color to white.border: 1px solid #91F726
: Adds a green border to match the theme.:focus
: Removes the default blue glow (box-shadow
) when the input field is focused.::placeholder
: Styles the placeholder text to a light gray (#777
) for a subtle effect.
4. Highlight Button (.btn-highlight
)
.btn-highlight {
background-color: #91F726;
color: #0E0F12;
border: none;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn-highlight:hover {
background-color: #7ED520;
transform: scale(1.05);
color: #0E0F12;
}
- Default State:
background-color: #91F726
: Bright green background.color: #0E0F12
: Dark text color for contrast.transition
: Smooth transition for background color and scaling.
- Hover State:
background-color: #7ED520
: Slightly darker green on hover.transform: scale(1.05)
: Enlarges the button slightly for an interactive feel.
5. Movie Cards (#movie-container .card
)
#movie-container .card {
background-color: #1A1A1D;
border: 1px solid #91F726;
transition: transform 0.3s, box-shadow 0.3s;
}
#movie-container .card:hover {
transform: scale(1.05);
box-shadow: 0 4px 15px rgba(145, 247, 38, 0.5);
}
- Default State:
background-color: #1A1A1D
: Dark background for cards.border: 1px solid #91F726
: Green border for consistency.transition
: Smooth animations for scaling and shadow effects.
- Hover State:
transform: scale(1.05)
: Enlarges the card slightly.box-shadow
: Adds a glowing green shadow for a dynamic look.
6. Modal Content (.modal-content
)
.modal-content {
background-color: #1A1A1D;
color: #fff;
border: 1px solid #91F726;
}
background-color: #1A1A1D
: Dark background for the modal.color: #fff
: White text for readability.border: 1px solid #91F726
: Green border for a cohesive design.
7. Spinner (.spinner-border
)
.spinner-border.text-highlight {
color: #91F726;
}
.spinner-border {
width: 3rem;
height: 3rem;
}
text-highlight
: Sets the spinner color to green.width
andheight
: Adjusts the size of the spinner to3rem
for better visibility.
body {
background-color: #0E0F12;
color: #fff;
font-family: 'Poppins', sans-serif;
}
h1 {
color: #91F726;
}
#search-input {
background-color: #1A1A1D;
color: #fff;
border: 1px solid #91F726;
}
#search-input:focus{
box-shadow: none;
}
#search-input::placeholder {
color: #777;
}
.btn-highlight {
background-color: #91F726;
color: #0E0F12;
border: none;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn-highlight:hover {
background-color: #7ED520;
transform: scale(1.05);
color: #0E0F12;
}
#movie-container .card {
background-color: #1A1A1D;
border: 1px solid #91F726;
transition: transform 0.3s, box-shadow 0.3s;
}
#movie-container .card:hover {
transform: scale(1.05);
box-shadow: 0 4px 15px rgba(145, 247, 38, 0.5);
}
.modal-content {
background-color: #1A1A1D;
color: #fff;
border: 1px solid #91F726;
}
.spinner-border.text-highlight {
color: #91F726;
}
.spinner-border {
width: 3rem;
height: 3rem;
}
Step 3 (JavaScript Code):
Finally, we need to create a function for a movie search application using the OMDb API in JavaScript. Here's a detailed explanation of the code:
1. API Configuration
const API_KEY = 'your_api_key'; // Replace with your API key
const API_URL = `https://www.omdbapi.com/?apikey=${API_KEY}&`;
API_KEY
: Stores the API key needed to access the OMDb API.API_URL
: Base URL for API requests, including the API key.
2. DOM Elements
const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');
const movieContainer = document.getElementById('movie-container');
const loading = document.getElementById('loading');
const movieModal = new bootstrap.Modal(document.getElementById('movieModal'));
searchInput
: Input field for the movie title.searchButton
: Button to trigger the search.movieContainer
: Container where movie results are displayed.loading
: Loading spinner shown during API calls.movieModal
: Bootstrap modal for displaying movie details.
3. Search Button Event Listener
searchButton.addEventListener('click', () => {
const query = searchInput.value.trim();
if (query) {
searchMovies(query);
} else {
alert('Please enter a movie title');
}
});
- Event Listener: Triggers when the search button is clicked.
- Validation: Checks if the input is not empty. If empty, alerts the user.
searchMovies(query)
: Calls the function to fetch and display movies.
4. Fetching Movies
async function searchMovies(query) {
movieContainer.innerHTML = '';
loading.style.display = 'block';
try {
const response = await fetch(`${API_URL}s=${query}`);
const data = await response.json();
loading.style.display = 'none';
if (data.Response === 'True') {
displayMovies(data.Search);
} else {
movieContainer.innerHTML = `<p class="text-center">No movies found for "${query}".</p>`;
}
} catch (error) {
loading.style.display = 'none';
console.error('Error fetching data:', error);
movieContainer.innerHTML = '<p class="text-center text-danger">Something went wrong. Please try again later.</p>';
}
}
- Clears Results: Empties the movie container and shows the loading spinner.
- Fetch API: Makes an API request with the search query.
- Error Handling:
- If the API call fails, logs the error and shows an error message.
- If no movies are found, displays a "No movies found" message.
5. Displaying Movies
function displayMovies(movies) {
movieContainer.innerHTML = movies
.map(movie => {
return `
<div class="col-lg-3 col-md-4 col-sm-6">
<div class="card">
<img src="${movie.Poster !== 'N/A' ? movie.Poster : 'placeholder.jpg'}" class="card-img-top" alt="${movie.Title}">
<div class="card-body">
<h5 class="card-title text-white">${movie.Title}</h5>
<p class="card-text text-white">Year: ${movie.Year}</p>
<button class="btn btn-highlight" onclick="fetchMovieDetails('${movie.imdbID}')">View Details</button>
</div>
</div>
</div>
`;
})
.join('');
}
- Maps Movies: Iterates through the movie list and generates HTML for each movie card.
- Placeholder Image: Uses a default image if the poster is not available (
Poster === 'N/A'
). - View Details Button: Calls
fetchMovieDetails()
with the movie's IMDb ID when clicked.
6. Fetching Movie Details
async function fetchMovieDetails(id) {
try {
const response = await fetch(`${API_URL}i=${id}`);
const movie = await response.json();
if (movie.Response === 'True') {
displayMovieDetails(movie);
} else {
alert('Unable to fetch movie details.');
}
} catch (error) {
console.error('Error fetching movie details:', error);
alert('Something went wrong. Please try again later.');
}
}
- API Call: Fetches detailed information about a specific movie using its IMDb ID.
- Error Handling: Alerts the user if the API call fails or no details are found.
7. Displaying Movie Details
function displayMovieDetails(movie) {
const movieDetails = `
<div class="d-flex">
<img src="${movie.Poster !== 'N/A' ? movie.Poster : 'placeholder.jpg'}" class="me-3" style="width: 200px;">
<div>
<h3>${movie.Title}</h3>
<p><strong>Year:</strong> ${movie.Year}</p>
<p><strong>Genre:</strong> ${movie.Genre}</p>
<p><strong>Director:</strong> ${movie.Director}</p>
<p><strong>Plot:</strong> ${movie.Plot}</p>
</div>
</div>
`;
document.getElementById('movie-details').innerHTML = movieDetails;
movieModal.show();
}
- HTML Structure: Creates a detailed view with the movie's poster, title, year, genre, director, and plot.
- Modal: Displays the details inside a Bootstrap modal.
const API_KEY = '5dde9039'; // Replace with your API key
const API_URL = `https://www.omdbapi.com/?apikey=${API_KEY}&`;
const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');
const movieContainer = document.getElementById('movie-container');
const loading = document.getElementById('loading');
const movieModal = new bootstrap.Modal(document.getElementById('movieModal'));
searchButton.addEventListener('click', () => {
const query = searchInput.value.trim();
if (query) {
searchMovies(query);
} else {
alert('Please enter a movie title');
}
});
async function searchMovies(query) {
movieContainer.innerHTML = '';
loading.style.display = 'block';
try {
const response = await fetch(`${API_URL}s=${query}`);
const data = await response.json();
loading.style.display = 'none';
if (data.Response === 'True') {
displayMovies(data.Search);
} else {
movieContainer.innerHTML = `<p class="text-center">No movies found for "${query}".</p>`;
}
} catch (error) {
loading.style.display = 'none';
console.error('Error fetching data:', error);
movieContainer.innerHTML = '<p class="text-center text-danger">Something went wrong. Please try again later.</p>';
}
}
function displayMovies(movies) {
movieContainer.innerHTML = movies
.map(movie => {
return `
<div class="col-lg-3 col-md-4 col-sm-6">
<div class="card">
<img src="${movie.Poster !== 'N/A' ? movie.Poster : 'placeholder.jpg'}" class="card-img-top" alt="${movie.Title}">
<div class="card-body">
<h5 class="card-title text-white">${movie.Title}</h5>
<p class="card-text text-white">Year: ${movie.Year}</p>
<button class="btn btn-highlight" onclick="fetchMovieDetails('${movie.imdbID}')">View Details</button>
</div>
</div>
</div>
`;
})
.join('');
}
async function fetchMovieDetails(id) {
try {
const response = await fetch(`${API_URL}i=${id}`);
const movie = await response.json();
if (movie.Response === 'True') {
displayMovieDetails(movie);
} else {
alert('Unable to fetch movie details.');
}
} catch (error) {
console.error('Error fetching movie details:', error);
alert('Something went wrong. Please try again later.');
}
}
function displayMovieDetails(movie) {
const movieDetails = `
<div class="d-flex">
<img src="${movie.Poster !== 'N/A' ? movie.Poster : 'placeholder.jpg'}" class="me-3" style="width: 200px;">
<div>
<h3>${movie.Title}</h3>
<p><strong>Year:</strong> ${movie.Year}</p>
<p><strong>Genre:</strong> ${movie.Genre}</p>
<p><strong>Director:</strong> ${movie.Director}</p>
<p><strong>Plot:</strong> ${movie.Plot}</p>
</div>
</div>
`;
document.getElementById('movie-details').innerHTML = movieDetails;
movieModal.show();
}
Final Output:
Conclusion:
Building a movie search app using HTML, CSS, and JavaScript is a great way to learn web development and API integration. This project demonstrates how to create a user-friendly interface, fetch data from an external API, and display dynamic results.
By following the steps in this guide, you now have a functional app that can search for movies and show their details. This project can be a foundation for more advanced applications, such as adding user authentication, saving favorite movies, or implementing search filters.
Keep experimenting with new features and projects to enhance your coding skills and create even more engaging applications.
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊