Projects

Movies/Series streaming platform

2023·7 min read

React
TypeScript
Tailwind CSS
Next.js
Live (opens in a new tab)GitHub (opens in a new tab)

Browse trending titles, dive into rich detail pages, and pick up exactly where you left off — all from a single keyboard-friendly UI.

Movies/Series streaming platform preview
On this page7 sections
  • 🎬 Reely
    • 🚀 Watch on Reely Space →
    • ✨ Features
    • 📲 Progressive Web App
    • 🏗️ Architecture
    • 🛠️ Tech Stack
    • 🙌 Credits

🎬 Reely#

Discover, track, and stream movies and TV shows — a fast, modern, TMDB-powered viewing experience.

🚀 Watch on Reely Space →#

Browse trending titles, dive into rich detail pages, and pick up exactly where you left off — all from a single keyboard-friendly UI.

Reely is a production-grade movie and TV show tracker built on the TMDB API. It is designed to feel native: prerendered to static assets for speed, animated for delight, and instrumented for serious frontend craftsmanship.

⭐ If Reely is useful to you, please star the repo.

✨ Features#

  • TMDB-powered catalog — trending, popular, top-rated, now-playing, and on-the-air movies and TV shows, pulled live from the TMDB API.
  • Animated hero slider — auto-rotating showcase of trending media on the landing page, with a muted trailer that plays inline while a slide holds the frame. Autoplay is opt-out via an on-slide toggle, and starts switched off on low-power devices so a weak phone isn't handed a video player and a full-screen Ken Burns at once. Your explicit choice always overrides that default.
  • Browse movies & TV — dedicated listing pages with a deep, live-applying filter system and infinite scroll (TanStack useInfiniteQuery + intersection observer).
  • Advanced filters — sort, tri-state genre pills (include / exclude / off), TMDB rating, vote count, runtime, release year (decade chips + range slider), original language, age rating (certification), and where to watch (streaming-provider logo grid + region picker). All filters live-apply on a 300ms debounce — no "Save" button — with an active-filter chips bar (tap to remove, or clear all) above the grid.
  • Rich detail pages — synopsis, cast, similar titles, recommendations, trailer, and ratings — fetched in a single TMDB append_to_response call.
  • In-page streaming player — watch movies and individual TV episodes via a configurable external source (see the in-app disclaimer).
  • Season & episode navigator — drill into any TV show by season and episode with a dedicated selector.
  • Trailers — YouTube trailer dialog plus a hero trailer preview.
  • Collections — franchise / collection pages that group a series of films.
  • Command-palette search (⌘K) — instant, debounced search across movies and TV via a cmdk dialog, with All / Movies / TV chips and remembered recent searches. No page reload.
  • Genre pages & chips — jump straight into any genre for movies or TV.
  • Real IMDb ratings (opt-in) — genuine IMDb scores layered over TMDB, served from prebuilt static shards (pnpm imdb:ratings) with a TMDB fallback. Behind NEXT_PUBLIC_IMDB_RATINGS and off by default: enriching list rows costs one TMDB subrequest per item, which overruns the Workers free-plan 50-subrequests-per-invocation cap.
  • Watchlist — save titles you want to watch, kept on your device (no account required).
  • Watch history — track watched movies and episodes, remove single items, or clear everything, on a dedicated page.
  • URL-synced filters — every active filter lives in the URL via nuqs, so filtered views are shareable and back-button friendly (transient UI chrome like open accordions stays out of the URL, keeping shared links clean).
  • Web Share — share any title through the native share sheet.
  • Privacy-first — watchlist, history, and recent searches live entirely in your browser; there's no login and nothing is stored on a server.
  • SEO & structured data — full JSON-LD (Website, Organization, CollectionPage, Breadcrumb), Open Graph, Twitter Cards, dynamic OG image generation, sitemap, and robots.
  • Accessible & responsive — skip-to-content, aria roles, a dedicated mobile nav, and a dark aurora UI that scales cleanly to phones.
  • Optimized imagery — ImageKit CDN with an automatic wsrv.nl → TMDB origin fallback chain, driven by a custom next/image loader so every image still ships a real srcset and each device downloads the size it actually renders.
  • Installable PWA — see the Progressive Web App section below.
  • Analytics built in — PostHog product analytics, client-side only, with every event centralized in lib/analytics.ts and build-time source-map upload so minified production stack traces symbolicate.
  • Deployed on the edge — see Architecture below.

📲 Progressive Web App#

Reely is a fully installable PWA — not just a manifest, but a working offline-resilient app shell.

  • Installable — "Add to Home Screen" on Android/Chromium (native beforeinstallprompt nudge) and iOS (Share → Add to Home Screen hint). Runs standalone, portrait, with a black theme.
  • App shortcuts — long-press the icon for Browse Movies and Browse TV Shows jump links.
  • Offline-resilient — a hand-written service worker (public/sw.js, no next-pwa/serwist/Workbox) with a per-request caching strategy:
    • immutable /_next/static/* assets → cache-first
    • page navigations → network-first, falling back to the last cached page, then a custom /offline.html
    • icons, manifest, and images → stale-while-revalidate
    • never cached: the streaming player, /api/*, RSC payloads, and any cross-origin request
  • Native touches — maskable icons (192 / 512), Apple touch icon, theme colors, browserconfig.xml / mstile, and a Safari pinned-tab icon.

Streamed video is live third-party content and is intentionally never cached for offline playback.

🏗️ Architecture#

Reely ships as a Next.js static export (output: 'export') on Cloudflare Workers Static Assets, plus one hand-written Worker for the parts that cannot be static. Next.js does not run in production at all.

This replaced an OpenNext deployment. Under OpenNext, 20–46% of Worker invocations were being killed on the free plan's 10ms CPU budget: any detail id outside the prerendered set re-rendered React on every request and could never become a cache hit, because Cloudflare will not edge-cache Worker-generated HTML. After the migration that figure is 0.0%, and p99 CPU went from ~700ms to ~8ms.

  • Static assets match before the Worker runs. Every prerendered page is a plain file: zero CPU, and it doesn't count against the invocations cap.
  • The Worker handles only what can't be static — /api/* (search, filtering, infinite scroll, season details, watch providers) and detail ids outside the prerendered set. A tail id costs one TMDB fetch plus an HTMLRewriter pass that injects the real title, OG/Twitter tags, JSON-LD, and a crawlable <h1>, so crawlers and unfurlers can't tell it from a prerendered page.
  • No Server Actions. A static export can't contain them; the browser talks to the Worker over HTTP instead, and the Worker calls the very same services/* functions the build calls.
  • caches.default is keyed by URL + Next build id, because cached fallback HTML references content-hashed chunks that the next deploy deletes.
  • The real ceiling on site size is the 20,000-file asset cap — a static export writes ~10 files per route, so route depth is tuned in lib/media-page.ts.

Detail pages use TMDB append_to_response so a whole page renders on one TMDB request, and every TMDB call goes through a single governed client (lib/fetch-client.ts) with a concurrency cap and 429 backoff.

🛠️ Tech Stack#

Layer Technology
Framework Next.js 16 (App Router, Turbopack, RSC, static export) + React 19
Language TypeScript 6
Styling Tailwind CSS 4 + tailwindcss-animate + @tailwindcss/typography
UI shadcn/ui + Radix UI, lucide-react icons, Framer Motion, sonner toasts
Data fetching TanStack Query 5 + React Server Components
URL state nuqs
Search cmdk command palette + use-debounce
Infinite scroll react-intersection-observer
PWA Web App Manifest + hand-written service worker (no next-pwa/serwist)
Data source TMDB API + optional IMDb ratings + ImageKit imagery
Streaming External, configurable video source
Analytics PostHog (client-side only)
Deployment Static export on Cloudflare Workers Static Assets + one hand-written Worker
Tooling ESLint, Prettier, Husky, Commitlint, Renovate

🙌 Credits#

Built by Mohamed Gado. Data and imagery courtesy of TMDB — Reely is not endorsed or certified by TMDB.

Buy Me A Coffee!

More projects

  • Books library2024 · React
  • Build your own ChatGPT2023 · React
  • JSON Converter2024 · React
All projects
  • 🎬 Reely
    • 🚀 Watch on Reely Space →
    • ✨ Features
    • 📲 Progressive Web App
    • 🏗️ Architecture
    • 🛠️ Tech Stack
    • 🙌 Credits