Technical Deep Dive

How We Built originalobjective.com: Headless CMS with a Modern React Frontend

A deep dive into how we built our own website using a headless CMS with a React frontend. Covers the architecture decisions, block grid rendering, type-safe API integration, and the lessons we learned along the way.

Laptop - Custom Software
Matt Perry - CTO

Curated by Matt Perry

CTO

7 March 2026

Why Headless Umbraco?

We have been building with Umbraco for years. It is a CMS we know inside out. But for our own site, we wanted something different from the traditional server-rendered approach.

The requirements were straightforward. We needed a content-managed website that loads fast, ranks well in search engines, and gives editors full control over page layouts through the block grid. We also wanted to use React and Tailwind CSS on the frontend because that is where our team works fastest.

Umbraco with its Delivery API gave us exactly that. The CMS handles content modelling, media management, and the block grid editor. Next.js handles rendering, routing, and performance. The two communicate through a typed REST API.

The Architecture

The setup is simple in principle:

Umbraco runs on .NET and serves as the content repository. Editors use the backoffice to create pages, manage block grids, upload media, and publish content. The Delivery API exposes all of this as JSON.

Next.js with the App Router fetches content from the Delivery API at build time and request time. Pages are statically generated where possible, with incremental static regeneration (ISR) keeping content fresh without full rebuilds.

Typed API clients are auto-generated from the Umbraco API specification. This means we get full type safety across the boundary between CMS and frontend. When a content type changes in Umbraco, we regenerate the client and TypeScript catches any mismatches at compile time.

Block Grid Rendering

The block grid is the core of the editing experience. Editors arrange content blocks in a 12-column grid, choosing from a library of block types we have built. Each block has content properties and optional settings properties for styling.

On the frontend, a central component mapper takes a block's content type alias and returns the correct React component. Each block component receives its content and settings as typed props.

The block grid wrapper iterates over the layout, resolves each block's component, and renders them with their content and settings. Settings typically control colours, backgrounds, and layout options, keeping styling separate from content.

Server Components by Default

Most of our block components are React Server Components. They render on the server with zero client-side JavaScript. This is a significant performance win because the majority of our blocks are static content: headings, text, images, and cards.

We only use client components where genuine interactivity is needed: forms, tabbed interfaces, search functionality, and animations. For adding animations to server components without making them client components, we built animation wrapper components that handle the motion logic on the client while keeping the content server-rendered.

Type-Safe API Integration

Auto-generating typed API clients from the CMS specification changed how we work. Instead of writing fetch calls by hand and hoping the response shape matches our types, we point our code generation tool at the API spec and it generates everything.

The generated client includes typed request functions, response models, and custom fetch wrappers. We added a custom fetch layer that handles error responses, caching headers, and revalidation consistently across all API calls.

Our workflow when content types change:

  1. Update the document type or data type in Umbraco
  2. Regenerate the TypeScript client
  3. Fix any type errors the compiler flags
  4. Update the block component if needed

This catches breaking changes at build time rather than in production.

Static Generation and ISR

Next.js changed how data fetching works in recent versions. The fetch function no longer caches by default, which caught us off guard initially. Pages that should have been static were rendering dynamically on every request.

We solved this with a combination of static parameter generation for pre-rendering pages at build time and revalidation settings on our fetch calls. Content updates appear quickly without requiring a full rebuild.

Image Handling

Images are stored in cloud blob storage through Umbraco's media system. On the frontend, we use the Next.js Image component with a custom loader that constructs the correct URL for Umbraco's image processor. This gives us automatic resizing, format conversion, and lazy loading.

The image loader handles both local development and production through environment-based URL configuration.

What We Would Do Differently

If we were starting again today, we would:

Automate content type management from day one. We built the initial content types and pages manually through the backoffice. Midway through the project, we started managing content types programmatically. It is dramatically faster and less error-prone, especially for creating block types with compositions.

Understand the caching model upfront. We spent too long debugging dynamic rendering issues that came down to framework defaults. Understanding the caching model from the start would have saved days.

Build fewer, more flexible block types. Some of our blocks could have been consolidated into more configurable versions. The maintenance overhead of many small blocks adds up.

The Stack

At a high level, here is what powers the site:

  • CMS: Umbraco (headless, .NET)
  • Frontend: Next.js with React
  • Styling: Tailwind CSS
  • API integration: Auto-generated TypeScript clients
  • Media: Cloud blob storage
  • Analytics: GA4 via Google Tag Manager
  • Cookie consent: GDPR-compliant consent management

If you are building a similar stack and have questions, get in touch. We are happy to share what we have learned.

Ready to put AI to work in your business?

Book a free 30-minute discovery call. We will discuss your goals, identify quick wins, and outline a practical plan to get started.

Book a discovery call

Subscribe to the AI Growth Newsletter

Get weekly AI insights, tools, and success stories - straight to your inbox.

Here is what you will get when you subscribe:

Subscribe to the AI Growth Newsletter
  • AI for SMBs - adopt AI without big budgets or complex setup
  • Future Trends - what is coming next and how to stay ahead
  • How to Automate Your Processes - save time with workflows that run 24/7
  • Customer Service AI - chatbots and agents that delight customers
  • Voice AI Solutions - smarter calls and seamless accessibility
  • AI News - how to stay ahead of the ever changing AI world
  • Local Success Stories - how AI has changed business in the UK

No spam. Just practical AI tips for growing your business.