Skip to content

PerfectoCayabyab/marquee

Repository files navigation

Marquee — a headless CMS film catalog

A film catalog rendered entirely from a headless CMS: Next.js server components query Sanity's content lake with GROQ — movies joined to their cast and crew, synopses rendered from Portable Text, and every poster served through a crop-aware image pipeline.

Runs against Sanity's public movies demo dataset (zp7mbokg), so the whole project works with zero credentials, zero backend, and zero environment variables — clone, install, run.

Home page

What it demonstrates

  • Headless CMS architecture — content lives in Sanity's content lake; the front end is a standalone Next.js app speaking GROQ over HTTP. No studio, no SDK — the API client is ~30 lines of fetch.
  • GROQ joins, both directions — movie pages dereference cast/crew (person->{...}), and person pages run a reverse join (*[_type == "movie" && references(^._id)]) to build a filmography with the person's roles resolved per film.
  • A hand-rolled Portable Text renderer — paragraphs, headings, blockquotes, strong/em/underline/code marks, link markDefs, and nested bullet/number lists rendered to React with no dependencies.
  • Crop-aware image pipeline — Sanity image refs (image-<id>-<w>x<h>-<fmt>) are parsed and turned into CDN URLs; editor crops become integer rect= params computed from crop fractions, composed with Next.js <Image> optimization.
  • Pure, tested core — ref parsing, crop math, Portable Text extraction, decade grouping, and initials live in src/lib/marquee.ts with a dependency-free test script (21 assertions) — including a regression test for a real bug where rounding crop offsets and dimensions independently produced a rect 1px outside the image, which the CDN rejects.

Screens

Movie page Person page
Movie page Person page

Stack

Architecture

Browser ──► Next.js (RSC, ISR 1h) ──► Sanity query API (GROQ over HTTP)
Posters ──► cdn.sanity.io (?rect=&w=&auto=format) ──► next/image optimizer
  • src/lib/sanity.ts — GROQ HTTP client (JSON-encoded $params, ISR caching) and the three queries: catalog, movie with cast/crew joins, person with reverse-reference filmography.
  • src/lib/marquee.ts — pure logic: parseImageRef, cropRect, imageUrl, portableTextToPlain, groupByDecade, initials.
  • src/components/PortableText.tsx — the dependency-free Portable Text renderer.
  • src/app/{page,movies/[slug],people/[slug]} — server components for every route.

Run it

npm install
npm run dev

No configuration needed. Tests, lint, and typecheck:

npm test        # pure logic (21 assertions)
npm run lint
npx tsc --noEmit

Pointing it at your own Sanity project

Set SANITY_PROJECT_ID and SANITY_DATASET to any dataset with public read access (or add a token header in src/lib/sanity.ts for private datasets). The GROQ queries target the standard movie-schema types (movie, person, screening) from Sanity's getting-started template.

License

MIT

About

Headless CMS film catalog - Next.js RSC + Sanity GROQ: cast/crew joins, hand-rolled Portable Text renderer, crop-aware image pipeline, Sanity public movies dataset

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors