Written by: Marlon Colca
Posted on 07 Sep 2025 - a month ago
nextjs typescript clones
Define a simple, typed catalog and helpers to query it efficiently from the UI.
Goal: Define a simple, typed catalog and helpers to query it efficiently from the UI.
src/data/movies.json: sample data (id, title, categories, sources, subtitles).src/lib/catalog.ts: TypeScript types and helper functions.// src/lib/catalog.ts (excerpt)
export type VideoSource = { src: string; type: string; label?: string };
export type SubtitleTrack = {
src: string;
lang: string;
label: string;
default?: boolean;
};
export type Movie = {
id: string;
title: string;
description?: string;
poster?: string; // local path or external URL
categories: string[]; // e.g. ["Destacados", "Demo"]
sources: VideoSource[]; // multiple quality/format options
subtitles?: SubtitleTrack[];
year?: number;
runtime?: number; // seconds
};
movies.json entry 📄{
"id": "tears-of-steel",
"title": "Tears of Steel",
"poster": "/posters/tears-of-steel.jpg",
"categories": ["Destacados", "Demo"],
"sources": [
{
"src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4",
"type": "video/mp4",
"label": "720p"
}
],
"subtitles": [
{
"src": "/subs/TOS-en.srt",
"lang": "en",
"label": "English",
"default": true
},
{ "src": "/subs/TOS-es.srt", "lang": "es", "label": "Español" }
]
}
// src/lib/catalog.ts (excerpt)
import movies from "@/data/movies.json";
export function getAllMovies(): Movie[] {
return movies as Movie[];
}
export function getMovieById(id: string): Movie | undefined {
return getAllMovies().find((m) => m.id === id);
}
export function getByCategory(): Record<string, Movie[]> {
const map: Record<string, Movie[]> = {};
for (const m of getAllMovies()) {
for (const c of m.categories || []) {
(map[c] ||= []).push(m);
}
}
return map;
}
sources: multiple qualities/formats; browsers pick the first compatible <source>. We’ll sort to prefer MP4 for reach.subtitles: Tracks can include .srt which we convert to .vtt at runtime.categories: string[]: Simple, flexible grouping; rows are derived directly from it.poster/description/year/runtime: Enrich UI without complicating the core model.movies.json is easy to edit/diff; with resolveJsonModule, TS understands imports and you still get types at usage sites.catalog.ts centralizes types and helper queries so UI doesn’t couple to file shape.rating, cast, or background as optional, then extend Movie accordingly.next/image need images.remotePatterns in next.config.ts.type matches encoding (video/mp4, video/webm) so source selection works..srt require CORS headers to be fetchable for conversion.public/videos/; use CDN for heavy media.public/videos/. Create a new category and verify it renders on the home page under its own row..srt subtitle to movies.json./watch/<id>: subtitles toggle and render correctly.
Build reusable components for the catalog grid and horizontal rows.
08 Sep 2025 - a month ago