Architecture

How HD CMS Deploys Static Sites to Vercel

SSG, deploy hooks, and why every page is pre-built HTML sitting on a CDN edge node

Adam Jackson 7 min read

Every website I build on HD CMS is a static site. Not a server-rendered site. Not a single-page app that fetches content on every page load. A fully pre-rendered, statically generated site where every page is plain HTML sitting on a CDN edge node, ready to be served instantly.

This is a deliberate choice. For the types of sites I build — brochure sites, small ecommerce, portfolio sites — there's no good reason to run a server. The content doesn't change on every request. It changes when someone updates it in the CMS. So the site should be rebuilt when content changes, and served statically the rest of the time.

Here's how the whole system works, from content update to live site.

What Static Site Generation Actually Means

Static site generation (SSG) means the entire website is built once at deploy time. Every page, every route, every piece of content is fetched from the CMS API and rendered into HTML files. Those HTML files are then deployed to Vercel's edge network — a global CDN with nodes in dozens of locations worldwide.

When a visitor requests a page, there's no server processing. No database query. No API call. The edge node nearest to the visitor serves the pre-built HTML file directly. Response times are typically under 50 milliseconds. The page is fully rendered — no loading spinners, no content flashing in after JavaScript loads.

Compare this to a traditional server-rendered site where every page request triggers a server process, a database query, template rendering, and then delivery to the visitor. Or a WordPress site where a PHP process runs on every page load, hitting a MySQL database, assembling the page from theme files and plugin outputs.

Static is faster by definition. There's nothing to compute. The work was already done at build time.

How the Build Works

The frontend sites are built with Nuxt 3, configured for full static generation with Vercel as the deployment target. The build process works in three stages:

1. Route discovery. A prerender:routes hook runs before the build. It calls the HD CMS public API to fetch all pages for the site, then registers each page's slug as a route to pre-render. This means the build automatically picks up new pages — if you add a page in the CMS, it's included in the next build without any code changes.

2. Link crawling. Nuxt's built-in link crawler (crawlLinks: true) follows every internal link from the routes discovered in stage 1. This catches any routes that aren't explicit pages — blog posts linked from a listing page, product detail pages linked from a catalogue, content type items linked from a content list block. If it's linked, it's pre-rendered.

3. Static generation. Nuxt renders every discovered route to static HTML. During this process, each page's content is fetched from the HD CMS API, the Vue components render the blocks, and the output is a complete HTML file with all content baked in. CSS is extracted, JavaScript is code-split, images get optimised references.

The result is a directory of HTML files — one per route — ready to be deployed to the edge.

Deploying From the CMS

This is the part that makes it practical for non-technical site owners. When content changes in the CMS, the site needs to rebuild. But you can't ask a restaurant owner to push to Git or run a terminal command.

The flow is: save content in the CMS → CMS triggers a rebuild → Vercel builds and deploys → site is live with new content. The site owner doesn't see any of this. They just save their changes and the site updates.

Under the hood, it works like this:

  1. Content is saved in the HD CMS admin panel
  2. The CMS calls the revalidation endpoint with the site ID
  3. The revalidation endpoint triggers a Vercel deploy hook — a URL stored in the site's settings that tells Vercel to start a new build
  4. Vercel pulls the latest code, runs the build (which fetches the latest content from the CMS API), and deploys the new static files
  5. Vercel's atomic deployment swaps the old files for the new ones instantly — no downtime, no partial updates

The deploy hook is just a URL. Vercel generates it when you set up the project. You paste it into the site settings in the CMS, and from that point on, content changes trigger rebuilds automatically.

Build Cache Considerations

There's one gotcha with deploy hooks that took some debugging. Vercel's build cache.

When you deploy via a Git push, Vercel detects that code has changed and runs a fresh build. But when a deploy hook fires with no code changes — which is what happens on a CMS content update — Vercel can use its build cache. The build output from the previous deployment is reused, including the pre-rendered HTML. Which means your content changes don't appear.

The fix is a single environment variable on the Vercel project:

VERCEL_FORCE_NO_BUILD_CACHE=1

This tells Vercel to always run a full build, even when no code has changed. The build takes a little longer (typically 30–60 seconds instead of 10–15), but the content is always fresh. For sites that update content a few times a week, the extra build time is negligible.

This is the kind of detail that doesn't appear in most "deploy to Vercel" tutorials. They assume your content is in your codebase, not in an external CMS. When your content lives in an API, you need the build cache disabled for deploy hooks to work properly.

Why Not Server-Side Rendering?

Nuxt supports SSR out of the box. Every page request runs through a serverless function, fetches content from the API, renders the HTML, and returns it. You can add ISR (Incremental Static Regeneration) to cache the output and regenerate in the background.

I've used SSR and ISR on projects where it makes sense — sites with thousands of pages where full SSG builds would take too long, or sites with genuinely dynamic content that changes on every request.

But for HD CMS client sites? SSR adds complexity without adding value:

  • Cost. Serverless functions cost money per invocation. Static files are served from CDN for effectively free. A brochure site on SSR might cost a few pounds a month in function invocations. Static is zero.
  • Cold starts. Serverless functions have cold start latency — the first request after a period of inactivity takes longer. Static files have no cold starts. Every request is instant.
  • Reliability. Static sites have virtually zero failure modes. The files exist on the CDN. They're served. SSR adds a function runtime, an API dependency, and potential timeout issues.
  • Simplicity. There's less to debug when something goes wrong. The HTML is right there. View source and you can see exactly what's being served.

For sites where content changes a few times a week and the owner just wants it to work, SSG is the right choice. Full stop.

The Performance Numbers

Static generation on Vercel's edge gives you performance that's hard to beat with any other approach:

  • Time to First Byte (TTFB): typically under 50ms from the nearest edge node
  • Largest Contentful Paint (LCP): consistently under 1.5 seconds on mobile
  • Cumulative Layout Shift (CLS): near zero — content is baked into the HTML, nothing shifts as JavaScript loads
  • Core Web Vitals: green across the board on PageSpeed Insights

These aren't best-case numbers on a fast connection. They're real-world numbers from client sites in production. The edge network means a visitor in London gets served from London. A visitor in New York gets served from New York. The content is the same — it's just closer.

For SEO, this matters. Google uses Core Web Vitals as a ranking signal. A static site on a CDN edge has an inherent advantage over a server-rendered site that needs to compute every response. You're starting from a faster baseline before you've even thought about optimisation.

The Full Picture

The deployment architecture for an HD CMS site is:

  • Content lives in HD CMS — a headless CMS backed by Supabase, hosted on Vercel
  • The frontend is a Nuxt 3 static site — pre-rendered at build time, deployed to Vercel's edge
  • Content changes trigger rebuilds — via deploy hooks, automatically from the CMS admin
  • The API is edge-cached — so builds are fast even with hundreds of pages
  • The output is plain HTML — no server runtime, no database dependency, no cold starts

Two Vercel projects. One for the CMS (serverless). One for the frontend (static). Connected by an API with edge caching and a deploy hook.

It's not flashy. There's no Kubernetes cluster. No microservices architecture diagram. No real-time this or serverless that. Just content in a CMS, HTML on a CDN, and a webhook that connects them.

That's the point. The best infrastructure is the kind you don't have to think about.

Want a Fast, Static Website?

HD CMS sites are pre-rendered and deployed to the edge. Fast, reliable, and simple to update.