Feature: CMS
The webapp uses Contentstack as its headless CMS for blog posts and location content. There is also an embedded Sanity Studio at /studio (legacy), but active content is served from Contentstack.
Contentstack Client
Configured in src/lib/contentstack/client.ts:
import contentstack from "@contentstack/delivery-sdk";
const stack = contentstack.stack({
apiKey: process.env.CONTENTSTACK_API_KEY || "",
deliveryToken: process.env.CONTENTSTACK_DELIVERY_TOKEN || "",
environment: process.env.CONTENTSTACK_ENVIRONMENT || "production",
host: "eu-cdn.contentstack.com",
});The client connects to the EU CDN (eu-cdn.contentstack.com).
Environment Variables
CONTENTSTACK_API_KEY=
CONTENTSTACK_DELIVERY_TOKEN=
CONTENTSTACK_ENVIRONMENT=productionBlog Posts
Blog pages live at src/app/[locale]/blog/:
page.tsx— Blog index with pagination[slug]/page.tsx— Individual blog post
Content Type: blog_post
Posts are queried with:
const result = await stack
.contentType("blog_post")
.entry()
.query()
.orderByDescending("published_at")
.find<Post>();Each post has these fields:
| Field | Type | Description |
|---|---|---|
uid | string | Contentstack unique ID |
title | string | Post title |
slug | string | URL slug |
excerpt | string (optional) | Short description |
cover_image | ContentstackAsset (optional) | Hero image |
published_at | string (optional) | Publication date |
region | "us" | "uk" | "both" (optional) | Region visibility |
Region Filtering
Posts are filtered by locale on the server:
posts.filter(
(p) => !p.region || p.region === "both" || p.region === locale,
);Posts without a region field or with region: "both" appear in both locales.
Caching
Blog pages set export const revalidate = 0 — they are not cached and always fetch fresh content.
Location Content
Location-specific content (university info, area descriptions) is managed through Contentstack via the location-content feature:
src/features/location-content/
api/
queries.ts # Contentstack queries for location data
components/
location-template.tsx # Template for location pages
university-cards.tsx # University info cards
area-cards.tsx # Area info cards
location-content-section.tsxSanity Studio (Legacy)
An embedded Sanity CMS Studio is available at /studio/[[...index]]. This appears to be a legacy integration. The route is a catch-all that serves the Sanity Studio UI.
Image Utilities
Contentstack image handling is available via src/lib/contentstack/image.ts, re-exported from the client module:
import { getImageUrl, type ContentstackAsset } from "@/lib/contentstack/client";