A movie search and discovery web app built with React and Vite, powered by the OMDb API — search any title, browse paginated results, view full movie details, and save favorites to a personal liked list stored locally in the browser.
Cafe Film lets a user search the OMDb catalog and browse results with pagination, open a dedicated details page for any title (poster, cast, ratings, plot, box office info), and like/unlike movies to build a personal collection under /likedMovies — all without a backend, using the browser's localStorage to persist likes between visits.
- Debounced live search — typing in the search bar queries OMDb automatically after a short pause, instead of firing a request on every keystroke
- Paginated results — next/previous controls that only appear when another page actually exists
- Movie details page — poster, release date, rating, runtime, genre, director, writers, cast, country, language, production, box office, awards and full plot, with a direct link to the title's IMDb page
- Liked movies — like/unlike any movie from its details page; liked titles are persisted in
localStorageand listed on a dedicated/likedMoviespage - Loading, empty and error states — the results view distinguishes between "still loading", "no results" and "the request failed", instead of silently doing nothing
- Toast notifications — like/unlike actions and fetch errors surface as toasts via
react-hot-toast - Dark mode — styling respects the user's OS-level color scheme via Tailwind's
dark:variants
- Language: JavaScript (React 19)
- Build tool: Vite
- Routing: React Router
- Styling: Tailwind CSS
- HTTP client: Axios
- UI feedback: react-hot-toast, react-icons
- Data source: OMDb API
cafe-film/
├── src/
│ ├── api/
│ │ └── omdb.js # Single source of truth for all OMDb requests
│ ├── hooks/
│ │ └── useDebouncedValue.js # Generic debounce hook used by the search bar
│ ├── utils/
│ │ ├── likedMovies.js # localStorage read/write/toggle for liked movies
│ │ └── toastStyle.js # Shared toast styling
│ ├── components/
│ │ ├── Header.jsx
│ │ ├── Navbar.jsx # Search input + link to liked movies
│ │ └── MovieList.jsx # Grid of movie cards
│ ├── Pages/
│ │ ├── HomePage.jsx # Search results / liked movies, with pagination
│ │ ├── MoviePage.jsx # Single movie details + like toggle
│ │ └── NotFoundPage.jsx
│ ├── App.jsx # Routes + data fetching/state
│ └── main.jsx # App entry point
└── public/
- Make sure you have Node.js 18+ installed
- Clone this repository
- Install dependencies:
npm install - Create a
.envfile in the project root (see.env.example) with your own OMDb API key:VITE_OMDB_API_KEY=your_omdb_api_key_here - Start the dev server:
npm run dev
The OMDb key is read from an environment variable (VITE_OMDB_API_KEY) instead of being hardcoded in the source, and .env is excluded from the repository via .gitignore. Use .env.example as a template for your own key.
Centralizing every OMDb call behind a small api/omdb.js module made it easy to keep the search page, the details page and the liked-movies list all consistent — one place to change the request shape or error handling instead of three. Debouncing the search input turned out to matter more than expected: without it, every keystroke fired a new network request, which both wasted API calls and caused results to flicker as older responses resolved after newer ones. Building the liked-movies page also meant fetching several movies by id in parallel rather than one at a time, which kept that page responsive as the liked list grows.