From-Scratch Build · Frontend
The public face of a lab website — a Next.js app that fetches all of its content from a separate headless CMS over an API and renders fast, SEO-friendly pages. Rebuilt from scratch to learn the modern decoupled web: a React frontend talking to a content backend.
What it is
This half of the site holds zero content. There are no hard-coded paragraphs, no lists of papers baked into the code. Instead, every page asks a separate content backend — a headless CMS — for its data over an API, then renders it with React.
I built it to understand the decoupled architecture that most modern sites use: presentation and content are two different applications. The editors work in the CMS; this app's only job is to fetch what they wrote and turn it into beautiful, fast pages. Next.js renders on the server so the first load is instant and search engines see real HTML.
The core idea I wanted to learn: separating content from presentation across two apps. The frontend can be rebuilt, redesigned or replaced without touching a single word the editors wrote — because the words live somewhere else entirely.
The stack
The point of this rebuild was the toolchain. Here is what each piece actually does in the system.
The React framework that does routing, server rendering and image optimisation. Pages can be rendered ahead of time or on each request.
Types describe the exact shape of the content coming back from the CMS, so a missing field is caught while coding, not in production.
A REST API on a separate backend supplies every piece of content — pages, people, posts — as JSON the frontend fetches.
The API URL and access key live in .env, so the same code points at a local CMS in development and a real one in production.
Image URLs come from the CMS and are loaded and optimised by Next.js's image pipeline at render time.
Self-hosted, optimised web fonts loaded with no layout shift — part of why a server-rendered page feels instant.
Architecture
Every page follows the same fetch-then-render path. Tracing it once made the decoupled model click.
A visitor opens a URL; Next.js matches it to a page component in the app router.
The page calls the CMS's REST endpoint with the configured URL and key, asking only for the content it needs.
The JSON is parsed into typed TypeScript objects, so the rest of the component knows exactly what fields exist.
React builds the HTML on the server; the browser receives a finished, indexable page.
React takes over the server-rendered HTML to make interactive elements live.
Remote images from the CMS are resized and served in modern formats automatically.
How it runs
Because the frontend is content-free, running it is mostly about telling it where the CMS is:
npm install pulls in Next.js, React and the toolchain..env.example and fill in the CMS API URL and key.npm run dev starts the dev server with hot reload against the live content.In my rebuild I focused on the data-fetching layer and the TypeScript types — once the shape of the content is pinned down, the components write themselves.
Reflection