A modern headless blog built with Umbraco CMS serving content via the Content Delivery API v2, with two frontend implementations consuming it.
| Layer | Technology | Role |
|---|---|---|
| Backend | Umbraco (.NET 10) | Headless CMS — content modelling, Delivery API, media |
| Frontend (SvelteKit) | SvelteKit + TypeScript + Svelte 5 | SSR rendering, Storybook visual testing |
| Frontend (Next.js) | Next.js 16 + TypeScript + App Router | SSR rendering, React Server Components |
| Styling | Tailwind CSS v4 | Utility-first CSS, class-based dark mode |
| Project | Role |
|---|---|
Application.Cms |
Umbraco host app — bootstraps CMS, Delivery API, Swagger, uSync, CORS. Uses SQLite. |
Application.Core |
Class library for custom backend logic. Feature-folder pattern under Features/. |
Application.Models |
Auto-generated ModelsBuilder models. Never edit files in Generated/ directly. |
openapi-typescript generates types directly from the live Swagger definitionstorybook-addon-vis (SvelteKit frontend)The backend uses SQLite — no database installation needed.
cd Application.Cms
dotnet run
| URL | Description |
|---|---|
https://localhost:44356/umbraco |
Backoffice |
https://localhost:44356/umbraco/swagger |
Swagger / API explorer |
| Field | Value |
|---|---|
[email protected] |
|
| Password | password12345 |
uSync imports all document type schema from source control on first boot — no manual backoffice setup required.
Tip: If you see a migration error on startup, delete
Application.Cms/umbraco/Data/Umbraco.sqlite.db(and its-wal/-shmsiblings) and restart. Umbraco will recreate the database from scratch and re-import schema via uSync.
cd Application.Cms
dotnet run # Run the CMS
dotnet build ../Application.slnx # Build all projects
# Frontend/.env
PUBLIC_UMBRACO_API_URL="https://localhost:44356"
cd Frontend
npm install
npm run dev # http://localhost:5173
# FrontendNextJs/.env.local
NEXT_PUBLIC_UMBRACO_API_URL="https://localhost:44356"
cd FrontendNextJs
npm install
npm run dev # http://localhost:3000
Both frontends use the same two-level dispatch pattern:
contentType of the response determines which page layout component renders (e.g. homepage, blogArticle)pageContent block listcontentType is looked up in componentMap to find the right block componentuSync/npm run generate-types in each frontend to update umbraco.d.tsMyBlock.svelte / MyBlock.tsx)componentMapMyBlock.stories.ts)| Thing | Convention | Example |
|---|---|---|
| Umbraco content type alias | camelCase | heroBlock |
| Component filename | PascalCase | HeroBlock.svelte / HeroBlock.tsx |
componentMap key |
camelCase alias | heroBlock |