AS.
← Back to work

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)
Moviebase — sign in

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.

moviebase-yts.netlify.app
Moviebase — Home carousels

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.

moviebase-yts.netlify.app
Moviebase — Movie detail

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.

moviebase-yts.netlify.app
Moviebase — Search

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.

moviebase-yts.netlify.app
Moviebase — Movie grid and pagination

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/*.json

Routing 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

React 18React Router 6styled-componentsSassFramer Motionreact-paginatereact-search-box@splidejs/react-splideaxios

LINKS

NEXT PROJECT

Shadow Sensei

→

05 / CONTACT

Let'sbuild
somethinggreat.