← all builds

From-Scratch Build · Frontend

Lab Website 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.

Next.jsReactTypeScript Headless CMS APISSR

What it is

A frontend with no content of its own

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

Tools under the hood

The point of this rebuild was the toolchain. Here is what each piece actually does in the system.

framework

Next.js

The React framework that does routing, server rendering and image optimisation. Pages can be rendered ahead of time or on each request.

language

TypeScript

Types describe the exact shape of the content coming back from the CMS, so a missing field is caught while coding, not in production.

data source

Headless CMS API

A REST API on a separate backend supplies every piece of content — pages, people, posts — as JSON the frontend fetches.

env config

Environment variables

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.

media

Remote images

Image URLs come from the CMS and are loaded and optimised by Next.js's image pipeline at render time.

fonts

next/font

Self-hosted, optimised web fonts loaded with no layout shift — part of why a server-rendered page feels instant.

Architecture

From CMS to rendered page

Every page follows the same fetch-then-render path. Tracing it once made the decoupled model click.

  1. Request a route live

    A visitor opens a URL; Next.js matches it to a page component in the app router.

  2. Fetch from the API live

    The page calls the CMS's REST endpoint with the configured URL and key, asking only for the content it needs.

  3. Type the response live

    The JSON is parsed into typed TypeScript objects, so the rest of the component knows exactly what fields exist.

  4. Render on the server live

    React builds the HTML on the server; the browser receives a finished, indexable page.

  5. Hydrate in the browser live

    React takes over the server-rendered HTML to make interactive elements live.

  6. Optimise media live

    Remote images from the CMS are resized and served in modern formats automatically.

How it runs

Point it at a backend and go

Because the frontend is content-free, running it is mostly about telling it where the CMS is:

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

What rebuilding it taught me