sdrgaard.dk

Portfolio

A headless portfolio built with Next.js and Umbraco, created as a personal project to explore modern frontend architecture and gain hands-on experience with technologies I wanted to work more with.

Next.jsReactTypeScriptTailwind CSSshadcn/uiUmbraco.NET CoreC#VercelSimply
01

The project

This portfolio is both a personal website and a playground for exploring new technologies.

Umbraco is used as a headless CMS, while Next.js handles the frontend. The two applications are hosted separately, with Umbraco running on Simply and the Next.js frontend deployed to Vercel.

The content is built from reusable components, allowing different page types and layouts to be assembled from the same set of building blocks. This keeps the frontend flexible while making it easy to create new pages and cases.

02

Architecture

The frontend is built with Next.js and React, consuming content from Umbraco through the Delivery API.

TypeScript is used throughout the frontend, with API types automatically generated from Umbraco's OpenAPI specification. This keeps the frontend models closely aligned with the API.

The CMS and frontend are deployed separately, with Umbraco hosted by Simply and Next.js hosted on Vercel.

03

Custom preview

Since the frontend is completely separate from the CMS, I implemented a custom preview flow between Umbraco and Next.js.

A custom Umbraco URL provider generates preview URLs for the corresponding Next.js route. The URL includes the content path and a shared token, allowing Next.js to request and render draft content without publishing it first.

04

Frontend

The frontend is built almost entirely with Tailwind CSS, with shadcn/ui used for a small number of UI components.

I wanted to explore how far I could take a utility-first approach while keeping the component structure simple and reusable.

For the site's scroll-based animations, I implemented a small custom Intersection Observer utility rather than introducing a dedicated animation library. It detects elements entering the viewport and applies the relevant animation state.