Cohorts Week 3: Movie Search App with React
First, create a fork and clone the project from Github. Clone as a folder called moviesearch:
git clone https://github.com/CSES-Open-Source/cohorts-wi26-week3-react.git moviesearchBefore starting, make sure you have Node.js v18+ installed. You can check by running:
node -vIf you see a version number like v23.3.0, you’re ready to continue.
If not, follow the steps below based on your operating system.
-
Install Node with Homebrew (recommended)
brew install node
-
Verify installation
node -v npm -v
You should now see version numbers for both Node and npm.
-
(Alternative) If you don’t use Homebrew, you can also download Node directly:
- Go to https://nodejs.org
- Choose LTS → macOS Installer (
.pkg) - Follow the on-screen setup wizard
-
Install Node using the official installer
- Visit https://nodejs.org
- Download the LTS version (
.msifile) - Run the installer
- Keep the default options checked — especially “Add to PATH”
-
Verify installation Open a new Command Prompt and run:
node -v npm -v
You should see version numbers appear.
-
(Optional for advanced users) You can also install via Chocolatey:
choco install nodejs-lts
Move into the starter folder within moviesearch and install dependencies:
cd moviesearch/starter
npm install
When you run npm install, a node_modules folder is created. This folder contains all the external code (libraries) your project needs to function.
You should always include node_modules in your .gitignore file to prevent it from being uploaded to your repository. Why?
- It is massive: This folder can contain thousands of files and take up a lot of space, which slows down your Git operations.
- It is redundant: Your package.json already lists every library the project needs.
- It is reproducible: Anyone who clones your repo can simply run npm install to recreate the exact same node_modules folder on their own machine.
You can start the development server to verify everything is working:
npm run dev
Follow the link (localhost:5173) to see the web app on a browser.
In components/MovieCard.jsx, complete the MovieCard component. So far, the component only displays information about the movie's title. Complete the component by adding information about
the movie's year, genre, and rating.
<p>Comedy</p>You should now be able to see a MovieCard for Ratatouille on the page.
In React, a prop (short for "property") is a way to pass data from a parent component to a child component. Props are read-only, meaning that a child component cannot modify the props it receives from its parent.
In MovieCard.jsx, update the MovieCard function by to accept the following props:
title, year, genre, rating
function MovieCard({ title, year, genre, rating }) {
}Now, you can access these variables within MovieCard. Replace all hardcoded values (like "Ratatouille") with variables.
<p>{genre}</p>In App.jsx, update the MovieCard that we are rendering to pass in values for title,
genre, and year, rating. These values are the props that the component uses.
<MovieCard title="Ratatouille" year={2007} genre="Comedy" rating={8.1} />In a real application, you won't want to manually type out every single component. Instead, you can use the JavaScript .map() function to "loop" through an array of data and transform each item into a React component.
In App.jsx, replace the single, hardcoded with a dynamic mapping of the DISNEY_MOVIES array. Note: At the top of App.jsx, DISNEY_MOVIES is imported from data/movies.js where we have mock data in the form of arrays.
<div className="movie-grid">
{DISNEY_MOVIES.map((movie) => (
<MovieCard
title={movie.title}
year={movie.year}
genre={movie.genre}
rating={movie.rating}
/>
))}
</div>On the page, you should now see a grid of components for all the movies in the DISNEY_MOVIES array.
The above code is a much more cleaner and efficient way of doing something like:
<div className="movie-grid">
<MovieCard
title="Ratatouille"
year={2007}
genre="Comedy"
rating={8.1}
/>
<MovieCard
title="Moana"
year={2016}
genre="Adventure"
rating={7.6}
/>
{/* repeat for all other movies in DISNEY_MOVIES.... */}
</div>To make our app interactive, we need a way to "remember" what the user is typing into the search bar. In React, we use a Hook called useState to create a state variable. Whenever this variable changes, React automatically re-renders the component to reflect the new data.
In App.jsx, initialize the state at the top of your App function. We will start with an empty string "" because the search bar is empty when the page first loads.
const [searchTerm, setSearchTerm] = useState("");searchTerm is the variable that holds the string value for what is searched.
setSearchTerm is a function that we call to change the value of searchTerm.
Note that the import of useState at the top of App.jsx is required to use it in our code.
Next, we need to "connect" this searchTerm state to your search bar <input> element so that what we type in the search bar is stored in searchTerm. You need to set the value to your state variable and update that state every time the user types using the onChange event.
valueis what we want the search bar text to be. In this case, we want it to be the same as the searchTerm value.onChangelets us specify what action should occur if the input changes. Here, we want to set searchTerm to be the value typed in the search bar.erepresents theonChangeevent, ande.target.valueis how we access the the string value of what was typed in the search bar (e.g. "Ratatouille")
<input
className="search-bar"
placeholder="Search for a movie..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>Now that we are "remembering" the search term in our state, the final step is to use that term to hide movies that don't match. We do this by applying the JavaScript .filter() method to our DISNEY_MOVIES array before we map over it.
With the filter method, we only want elements in DISNEY_MOVES that include the search term in the movie title. We will also use .toLowerCase() on both the search term and the movie title so that searching for "ratatouille" or "RATATOUILLE" still finds "Ratatouille".
.filter(movie => movie.title.toLowerCase().includes(searchTerm.toLowerCase()))We can chain the filter method with the map method as shown below:
{DISNEY_MOVIES
.filter(movie => movie.title.toLowerCase().includes(searchTerm.toLowerCase()))
.map(movie => (
<MovieCard
key={movie.id}
title={movie.title}
year={movie.year}
genre={movie.genre}
rating={movie.rating}
/>
))
}The above code first filters out movies with titles that do not match the search term. Then, we are mapping the selected movies to MovieCard components which will be rendered on our page.
Now, when you type a movie title in the search bar, the grid of movies should be automatically filtered.
Right now, we only display a grid of Disney movies. Allow the user to click buttons to switch between viewing a grid of Disney movies and a grid of Harry Potter movies.
Note: array with Harry Potter movies data can be found in data/movies.js and imported the same way
that DISNEY_MOVIES was imported in App.jsx
Steps:
- In a file
components/button.jsx, create a reusable button component with a prop for the button's text (e.g. Disney, Harry Potter). You can also add CSS for the button in App.css. - In App.jsx, display one button component with the text "Disney" and one with "Harry Potter"
- When the "Disney" button is clicked, we will use
DISNEY_MOVIESfor our movies list. When the "Harry Potter" button is clicked, we will useHARRY_POTTER_MOVIESfor our movies list. Implement this usinguseState()to hold the correct movies array and theonClickevent in the buttons.
Hint for #3: The button component (child) can't reach "up" into the parent component App (located in App.jsx) to change the movie array state variables directly. Instead, pass a function down to the button component as a prop. When the button component triggers that function during onClick events, it's like sending a signal back up to App.jsx to update the state!
To upload your work to your GitHub repository, follow these steps:
In your terminal, navigate to the directory of your project and run the following command to stage all your changes:
```bash
git add .
Next, commit your changes with a descriptive message:
git commit -m <message>Finally, push your changes to your GitHub repository:
git push origin mainThis will upload your committed changes to the main branch of your remote repository on GitHub.
You have successfully completed the React Movie Search App project! 🎉