10 / 11
Frontend
Moviebase
A YTS clone that browses, searches, and links out to real torrent downloads.
- ROLE
- Full-stack developer, solo project
- BUILT FOR
- Personal project / portfolio piece
- STACK
- React 18, React Router 6, styled-components + Sass, Framer Motion, react-paginate, react-search-box, @splidejs/react-splide, axios, Create React App
- PLATFORM
- Web app (client-side only, deployed to Netlify)

THE PROBLEM
YTS exposes a public JSON API for its movie torrent catalog, but no first-party way to browse it as a modern web app — just the API and the original site. This project scratches the "build a real front end against a real third-party API" itch: pagination, search, routing, animated transitions, loading states — the standard product-UI problems — applied to data that's actually interesting (posters, ratings, genres, torrent files) rather than another to-do list.
It's also a practical exercise in working around a public API that doesn't set CORS headers for browser clients: every request has to be proxied.
THE IDEA
Keep it a thin, purely client-side React app: fetch directly from the YTS API (through a CORS proxy), render it with a small set of reusable components (card, carousel, pagination, search), and let React Router handle the home/listing/detail/search flows with animated page transitions via Framer Motion.
No backend, no database — the YTS API is the only source of truth.
WHAT IT DOES
01
Home carousels
Landing page with a hero heading and two carousel rows — "Top Animated Movies" and "Recently Added" — each built on @splidejs/react-splide and backed by its own YTS query.

02
Movie detail
Fetches full movie details — poster, background art, rating, runtime, genres, embedded YouTube trailer — and renders every available torrent as a quality/type button (720p.Web, 1080p.Bluray, etc.) linking straight to the magnet/torrent URL.

03
Search
A controlled input pushes a /movies/search/:term route, letting the movie grid's existing data-fetching effect do the actual querying — the same page serves both the full listing and search results.

04
Movie grid and pagination
A responsive grid of movie cards with a loader while fetching, and react-paginate-driven pagination computed from the API's total movie_count — paging, searching, and loading states all stay in sync through one loadMovies callback.

HOW IT WORKS
React SPA (CRA, client-side only)
│
├─ components: MovieCard, MovieCarousel, Pagination, Search, Loader, Btn
├─ pages: Home, Movies (+search), Movie (detail)
└─ services/movieService.js
│ fetch(wrapped URL)
▼
api.allorigins.win ──(CORS proxy)──> yts.mx/api/v2/*.jsonRouting every call through a CORS proxy
Since yts.mx's public API doesn't allow cross-origin browser requests, every call is routed through api.allorigins.win, which wraps the response as { contents: "<json string>" } that then has to be JSON.parse'd again before use.
Two fetch paths, one service file
movieService.js has fetchMoviesFromAPI for the paginated/search grid (uses allorigins' /get + double-parse), and fetchMovies for the home carousels (uses the /raw passthrough, and keeps a simple in-memory movieCache object keyed by carousel title so switching pages doesn't re-fetch "Recently Added" every time).
Animated transitions without per-page boilerplate
All routes are wrapped in Framer Motion's AnimatePresence keyed on location, giving fade transitions between Home, Movies, and Movie detail without extra per-page boilerplate.
CHALLENGES AND WHAT I LEARNED
No CORS on the public API
Every YTS endpoint had to be proxied through a third-party CORS-unlocking service, which also meant handling an extra layer of JSON wrapping/parsing and accepting that service as a single point of failure for the whole app.
Pagination against someone else's API
react-paginate's page count has to be derived from YTS's movie_count field rather than a local dataset, so paging, searching, and loading states all had to stay in sync through one callback.
Mixed styling approaches
The app ended up with both styled-components and per-component .scss files rather than standardizing on one — workable at this size, but something a larger version of the app would need to reconcile.
RESULTS
Full browse/search/paginate/detail flow built against the live YTS v2 API.
3 route pages (Home, Movies/search, Movie detail) plus 8 reusable components.
Client-side only — no backend, hosted as a static build on Netlify.
Animated route transitions via Framer Motion's AnimatePresence, with in-memory caching for the two home-page carousel queries.
STACK
LINKS

NEXT PROJECT
Shadow Sensei
05 / CONTACT