> ## Documentation Index
>
> Fetch the complete documentation index at: https://superpowers.frontis.nl/llms.txt
> Use this file to discover all available pages before exploring further.

---

title: Umbraco Block List blocks
summary: Frontis guidance for coding agents building Umbraco Block List blocks - backoffice tree structure, document type rules, data type and content wiring, layouts, images, compositions and the strongly typed ViewComponent code.
order: 60

---

# Block List blocks

Use this guide when adding, changing or reviewing a Block List block in a Frontis Umbraco project.

This document is written for coding agents. It is the single source of truth for Block List work and expands on the Umbraco development guide. When they conflict, this document wins for Block List work.

**Creating a new block? Follow the "Execution procedure" section step-by-step.** That section is the mandatory, ordered sequence. The reference sections below it (Backoffice tree structure, Document type rules, Naming data types, etc.) explain the detailed rules — the procedure tells you **when** to apply each one. Do not skip steps, do not reorder them.

## When this guide applies

This guide applies ONLY to Frontis Umbraco projects (see the Umbraco development guide for how a repository is identified as one). It does not apply to ABP, Nuxt or other non-Umbraco repositories.

Page content in a Frontis Umbraco project is composed from Block List blocks rendered by `Frontis.Umbraco.BlockList` (strongly typed ViewComponents with assembly-scanning auto-discovery, registered with `.AddFrontisBlocklist(...)`).

## Source-of-truth priority

Use the same priority order as the Umbraco development guide:

1. explicit user instruction;
2. local repository files (`AGENTS.md`, `.frontis/*`, `Examples/` folders, the existing backoffice tree and uSync files);
3. this document;
4. existing code and content structure in the repository;
5. general Umbraco knowledge.

The rules below encode recurring mistakes. Never invent folder names, tab names or naming conventions from memory — always mirror the structure that already exists in the repository's backoffice tree and uSync files.

## Before you start — clarify and spec

Do NOT jump straight into creating document types. Block List work is non-trivial Superpowers work.

1. **Confirm you are on a feature branch in a worktree** — never work on `main` or `dev` (see AGENTS.md and the Azure DevOps guide).
2. **Read `AGENTS.md` and `.frontis/*` first.** If you have not read them in this session, read them now.
3. **Ask the block-design questions** (see below) before creating anything.
4. **Write a short spec.** Save it as a markdown file under `docs/superpowers/specs/` in the target repository (create the folder if it does not exist), named after the block (for example `docs/superpowers/specs/block-list-text-and-image.md`). The spec captures: block name, fields, tabs, compositions used, whether settings are needed, layouts, and where each artifact lives in the backoffice tree. Get agreement on the spec before building.

### Questions to ask before building a block

Ask the user these before creating document types, because the answers change the structure:

- **Settings step** — content blocks are scaffolded by default with a background (`bg`) option and a `width` in the settings document type. Ask: *do you need a settings block at all, and do you need `bg` / `width`?* Do not create a settings document type or those fields unless the user confirms them.
- **Compositions step** — ask which compositions the block should use, and in what order.
- **Images** — for any image field, ask: *should the image be able to contain a link, and should the aspect ratio be configurable?* Link and aspect are provided by **compositions**, not by ad-hoc fields on the block. Add the relevant image composition(s) instead of inventing new properties.
- **Layouts** — ask whether the block needs multiple layouts and how many.

## Execution procedure — mandatory step order

This is the single authoritative execution sequence for creating a Block List block. Follow these steps **top-to-bottom, in this exact order**. Do NOT skip steps, do NOT reorder them, and complete each step before starting the next. Conditional steps are marked — skip them only when the condition is false.

The reference sections below (Backoffice tree structure, Document type rules, Naming data types, etc.) contain the detailed rules. This procedure tells you **when** to apply them.

### Phase 1 — Backoffice: document types and data types

1. **Find the `Block List` folder** in the document types tree.
2. **Create the block's folder** — create a child folder named after the block (for example `MyNewBlock`) inside the `Block List` folder.
3. **Create the content document type** `blockListMyNewBlock` **inside that folder** — apply every rule from "Document type rules": no description, "Vary by culture" off, `Is an Element Type` on, compositions at the top, single `Content` tab with alias `content`, correct sort orders.
4. *(only when settings are confirmed)* **Create the settings document type** `blockListMyNewBlockSettings` **inside the same folder** — same rules apply.
5. *(only when layouts are confirmed)* **Create the `Layouts` folder** inside the block's folder (for example `Document Types/Block List/MyNewBlock/Layouts/`).
6. *(only when layouts are confirmed)* **Create each layout element type** (`blockListMyNewBlockLayout1`, `blockListMyNewBlockLayout2`, ...) **inside the `Layouts` folder** — see "Layouts" for naming rules.
7. **Create or reuse data types** for any custom properties — follow "Naming data types" for the naming pattern. Place custom Block List data types in the `Frontis` data types folder.
8. **Verification gate** — confirm all document types are in the correct folders (content + settings inside `Block List/{Name}/`, layouts inside `Block List/{Name}/Layouts/`) and no document type is in the root. Review the document type rules checklist items before continuing.

### Phase 2 — Wiring into content

9. **Add the block to the Block List data type** — register the new block (and its settings, if applicable) in the relevant Block List data type so editors can select it. See "Wiring the block into content".
10. **Wire the block into the page content Block List and rich-text link** — add the block to the Block List used on pages, and add it to the rich-text link configuration.

### Phase 3 — Thumbnail

11. **Create the thumbnail image** — download or generate a placeholder thumbnail following the naming and location rules in "Block thumbnails". Place it in `wwwroot/assets/images/blocklist/`.
12. *(only when multiple layouts)* **Create additional layout thumbnails** — `{block-name}-layout2.jpg`, `{block-name}-layout3.jpg`, etc.
13. **Select the thumbnail on the block** in the Block List data type configuration (the Thumbnail setting where the block is configured).

### Phase 4 — Code: ViewComponent, view and model

14. **Create the code folder** `BlockList/Blocks/MyNewBlock/` in the Web project.
15. **Add the ViewComponent** `BlockListMyNewBlockViewComponent.cs` — see "Code patterns" for the class template.
16. **Add the Razor view** `BlockListMyNewBlock.cshtml` in the same folder — the `@model` must match the ViewComponent's view model type.
17. *(only when computed properties are needed)* **Add the view model** `BlockListMyNewBlockViewModel.cs` — extend `BlockListViewModel<TContent, TSettings>`, pass it as the third type parameter on the ViewComponent, and override `ExtendViewModel()`.
18. **Regenerate models and build** — start or restart the site so ModelsBuilder regenerates, then verify the solution builds without errors.

### Phase 5 — Preview page

19. **Find the `Block List` page** in the content tree.
20. **Create a child content page** under it using the `content` document type, named after the block.
21. **Hide the preview page** — set `umbracoNaviHide`, `excludeFromSearchEngines` and `excludeFromInternalSearch` all to `true`.
22. **Add the block with dummy data** — add one content block per layout so every layout is visible on the preview page.
23. **Sort the child pages** under the `Block List` page alphabetically.
24. **Add a link to the preview page** from the text element on the `Block List` page, in the correct alphabetical position.

### Phase 6 — Commit

25. **Commit uSync files and regenerated models together** — see the Umbraco development guide.
26. **Run the checklist** at the end of this document — every box must be checked before the work is done.

## Backoffice tree structure

Placement mistakes are the most common Block List error. Put every artifact in the correct place in the backoffice tree; mirror the names already used in the repository.

### Create the folder before the document type

When creating a document type via the MCP server or the Management API, Umbraco places it in the **document-types root** unless you explicitly specify a parent folder (container). Always create or locate the correct parent folder **first**, then create the document type **inside** it.

1. **Find or create the block's folder** — look up the `Block List` folder, then find or create a child folder named after the block (for example `MyNewBlock`) inside it.
2. **Create the document type inside that folder** — pass the folder's ID as the parent when creating the document type. Never create a document type without specifying a parent; it will end up in the root.
3. **For layout element types**, find or create a `Layouts` folder inside the block's folder, then create each layout element type inside that `Layouts` folder.

This folder-first rule applies every time — content blocks, settings blocks and layout element types all require the correct parent folder to exist before the document type is created.

Document types:

- **The block's content block goes in its own folder with the correct name, inside the `Block List` folder.** Never leave a block document type in the document-types root or directly under another folder.
- **Element types (for example cards, assortment items) go in the `_Elements` folder inside the `Block List` folder** — not in the block's own folder and not loose in the `Block List` folder.
- **Layout element types go in a `Layouts` folder inside that block's folder** (see Layouts below).

Data types:

- **Custom Block List data types (for example a `cards` Block List) go in the `Frontis` folder** in the Data Types tree — not in the Data Types root.

Intended shape (follow the exact names already present in the repository):

```text
Document Types/
  Block List/
    _Elements/                     <- shared element types (cards, assortment items, ...)
    MyNewBlock/                    <- the block's own folder (correct name)
      blockListMyNewBlock
      blockListMyNewBlockSettings  <- only when settings are needed
      Layouts/                     <- layout element types for this block
        blockListMyNewBlockLayout1
        blockListMyNewBlockLayout2

Data Types/
  Frontis/
    <custom Block List data types, e.g. Cards>
```

## Document type rules

- **Naming** is always `blockList{Name}` / `blockList{Name}Settings` (aliases camelCase, generated models PascalCase `BlockList{Name}`). Element types are `element{Name}`; compositions are `composition{Name}`.
- **Never add a description** to any document type you create — leave the description field (the field directly under the name of an element/document type) empty.
- **Turn "Vary by culture" off** on every new document type used as a Block List block (both the content and the settings document types). This applies regardless of whether the project has one or multiple cultures — new Block List document types must never vary by culture.
- **Put the composition group/tab at the top.** When a block uses compositions, the composition-provided group must be ordered above the block's own groups — the `Composition` heading belongs at the top of the document type, not at the bottom.
- **Keep everything on one Content tab.** The `title`, `text` and `button` fields belong on a single `Content` tab. When new fields are added they must go on that existing `Content` tab. Do NOT let new fields land on a separate `Generic` tab — that produces two tabs where there should be one. If a `Generic` tab appears, move the fields onto the `Content` tab and remove the empty `Generic` tab.
  - **Why `Generic` appears.** Umbraco merges tabs/groups across compositions by **alias**, not by display name. The `Content` tab you see usually comes from a composition (alias `content`). If you add a property while you are not inside that tab, Umbraco creates a default group with alias `generic`, which renders as its own separate `Generic` tab. Two different aliases (`content` vs `generic`) always render as two tabs; they only merge when the alias is identical.
  - **Correct order — create the tab first, then the fields.** Do NOT add the property first and move it afterwards (it lands in `Generic` and you then have to clean up). Instead: first ensure the block itself has a `Content` tab/group with the **same alias (`content`)** as the composition's tab, then create every new field directly inside that tab. In uSync, verify the new property's `Tab`/group alias is `content` (not `generic`) and that no empty `generic` group is left behind.
- **Set the sort order so new fields sit below the compositions — only after the tab/alias is correct.** First apply the Content-tab rule above (new fields in the `content` group, no stray `generic` group); sort order only behaves predictably once that is fixed. Composition-provided properties (`heading`, `text`, `button`, `image`, etc.) keep the lower `sortOrder` values and stay at the top. Any new field you add must get a **higher `sortOrder` number** than every composition property, so it renders underneath them. Never give a new field a sort order that pushes it above the composed fields.
  - **`sortOrder` is scoped per group/tab, not global.** Ordering is only compared *within the same group*: the first property in a group is `sortOrder` `0`, the next `1`, and so on. If your new field lands in a different group (for example the auto-created `generic` group), it starts its own counter at `0` again — that is why you can see two fields both on `0`. A higher number only pushes the field below `title`/`text`/`button` when the field lives in the **same group with the same alias (`content`)** as those composition properties. So fix the group/alias first (the Content-tab rule above), then set a `sortOrder` higher than the highest composition property in that group.

## Naming data types

Custom data types (and per-field properties that get their own data type) get a structured, human-readable name built from these parts, in this order, separated by ` - `:

```text
{property type} - {block list / document type} - {Settings | empty} - {field name}
```

- **property type** — the kind of property/component, for example `Block List`;
- **block list / document type** — which block or document type it belongs to, using only the block's **own name** (for example `Text swiper`) — do NOT append the field name here;
- **Settings or empty** — use `Settings` when the property lives on the settings document type; leave this part out entirely when it lives on the content document type;
- **field name** — the property/field name, for example `Layout`.

Every part is a **separate segment** joined by ` - `. Never merge the block name and the field name into one segment.

Example (a field named `Items` on the `Text swiper` block):

```text
Block List - Text swiper - Items        <- correct (block name and field name are separate segments)
Block List - Text swiper items          <- WRONG (block name and field name merged into one segment)
```

Example (a field on the settings document type):

```text
Block List - Video highlight - Settings - Layout
```

The same field on the content document type omits the `Settings` part:

```text
Block List - Video highlight - Layout
```

## Wiring the block into content

A block is not finished until it is reachable from the page. After creating the document type:

1. **Add the block to the Block List data type immediately.** Register the new block in the relevant Block List data type so it can be selected.
2. **Add the block to the page content Block List and to the rich-text link.** Wire it into the Block List used by content editors, and add the rich-text (link) configuration so it can be inserted there too.
3. Commit the uSync files together with the regenerated models (see the Umbraco development guide).

## Block thumbnails

Every block needs a thumbnail image, and it must be **created and then selected on the block in the Block List data type** (the block's Thumbnail setting where the block is configured).

- **Naming.** Name the thumbnail after the block, **all lowercase with words separated by dashes**, ending in `.jpg`: `{block-name-in-lowercase-with-dashes}.jpg`. For a block named `Text swiper` the file is `text-swiper.jpg`.
- **Single layout / layout 1** uses this base name (`text-swiper.jpg`).
- **Multiple layouts.** Layout 1 keeps the base name; from **layout 2 onwards append `-layout{N}`**: `text-swiper-layout2.jpg`, `text-swiper-layout3.jpg`, and so on. Do not add `-layout1` to the first layout.
- **Location.** Place the thumbnail files in `wwwroot/assets/images/blocklist/` in the Web project, alongside the existing block thumbnails (`text.jpg`, `text-multiple.jpg`, `image-multiple.jpg`, `text-and-image.jpg`, `cards.jpg`, ...). Do not invent a new folder — mirror this existing location.

### Generating a placeholder thumbnail

When there is no final artwork yet, generate a placeholder with [dummyimage.com](https://dummyimage.com) and download it straight into the thumbnail folder. The URL format is:

```text
https://dummyimage.com/{width}x{height}/{background}/{textColor}.jpg&text={text}
```

- **size** `600x400` (match the existing thumbnails);
- **background** `1b264f` and **text color** `fff` (hex without `#`);
- **text** uses `+` for spaces.

Download it with `Invoke-WebRequest`, naming the file per the rules above:

```powershell
Invoke-WebRequest `
  -Uri "https://dummyimage.com/600x400/1b264f/fff.jpg&text=text+swiper" `
  -OutFile "wwwroot/assets/images/blocklist/text-swiper.jpg"
```

For an extra layout, change only the text and the file name (`-layout2`, `-layout3`, ...):

```powershell
Invoke-WebRequest `
  -Uri "https://dummyimage.com/600x400/1b264f/fff.jpg&text=text+swiper+layout+2" `
  -OutFile "wwwroot/assets/images/blocklist/text-swiper-layout2.jpg"
```

## Layouts

When a block has multiple layouts:

- **Name layouts `Layout 1`, `Layout 2`, ...** and name the layout property/alias `blockList{Name}Layout1`, `blockList{Name}Layout2`, and so on.
- **Create the `Layouts` folder first, then the layout element types inside it.** Find the block's own folder (for example `Document Types/Block List/MyNewBlock/`), create a child folder named `Layouts` inside it, and only then create each layout element type with that `Layouts` folder as its parent. Do not create layout element types without specifying a parent — they will end up in the document-types root.
- **Branch on the layout in the view using the generated model's `ModelTypeAlias`**, never a magic string:

```cshtml
@if (Model.Layout == BlockListTextAndImageLayout4.ModelTypeAlias)
{
    <!-- layout 4 markup -->
}
```

## Code patterns

The execution procedure (Phase 4) tells you when to create each file. This section provides the code templates and rules.

All code lives in `BlockList/Blocks/MyNewBlock/` in the Web project.

**ViewComponent** (`BlockListMyNewBlockViewComponent.cs`):

```csharp
public class BlockListMyNewBlockViewComponent
    : BlockListViewComponentBase<BlockListMyNewBlock, BlockListMyNewBlockSettings>;
```

**View** (`BlockListMyNewBlock.cshtml`):

```cshtml
@model BlockListViewModel<BlockListMyNewBlock, BlockListMyNewBlockSettings>
```

**View model** (only when computed properties are needed) — `BlockListMyNewBlockViewModel.cs` extending `BlockListViewModel<TContent, TSettings>`, passed as the third type parameter on the ViewComponent, override `ExtendViewModel()`.

Code rules:

- naming is always `BlockList{Name}` / `BlockList{Name}Settings` / `BlockList{Name}ViewModel`;
- the `@model` in the `.cshtml` MUST match the ViewComponent's view model type;
- do not use the obsolete `BlockDefinitionBase`;
- document every block with a short class summary;
- render block lists in page views with `<vc:block-list model="@Model.ContentBlockList" />`.

## Create the block preview page

The execution procedure (Phase 5) tells you when to create the preview page. This section provides the detailed sub-steps:

1. **Find the `Block List` page** in the content tree — the parent page that collects all block preview pages.
2. **Create a child content page under it** using the `content` document type, named after the block (the same name as the block).
3. **Hide the preview page from navigation and search** via its SEO/visibility settings (use the technical property aliases, not the backoffice labels):
   - `umbracoNaviHide` = `true`;
   - `excludeFromSearchEngines` = `true`;
   - `excludeFromInternalSearch` = `true`.
4. **Add the block to the page's Block List with dummy data.** Use the content from the supplied screenshot/design where available. **Multiple layouts means multiple content blocks** — add one content block per layout so every layout is visible on the preview page.
5. **Sort the child pages under the `Block List` page alphabetically.**
6. **Add a link to the new preview page from the text element on the `Block List` page**, inserting it in the correct **alphabetical position** among the existing links.

## Anti-patterns

Avoid:

- starting to build before writing a spec under `docs/superpowers/specs/`;
- creating a settings document type or `bg` / `width` fields without confirming they are needed;
- leaving a block document type in the document-types root instead of its own folder inside `Block List`;
- putting element types anywhere other than the `_Elements` folder;
- putting layout element types anywhere other than the block's `Layouts` folder;
- putting custom Block List data types anywhere other than the `Frontis` data types folder;
- adding a description to a document type;
- leaving "Vary by culture" enabled on a new Block List content or settings document type, even when the project has multiple cultures;
- placing the composition group at the bottom instead of the top;
- giving new fields a sort order lower than the composition properties, so they appear above the composed fields;
- splitting fields across a `Content` tab and a `Generic` tab;
- adding image link/aspect as ad-hoc fields instead of using the image compositions;
- naming layouts or layout properties inconsistently with `Layout 1` / `blockList{Name}Layout1`;
- naming thumbnails inconsistently — anything other than lowercase-with-dashes `.jpg`, or adding `-layout1` to the first layout instead of starting the suffix at `-layout2`;
- branching on layouts with magic strings instead of `ModelTypeAlias`;
- forgetting to add the new block to the Block List data type, the content Block List and the rich-text link;
- block components that do not follow the `BlockList{Name}` naming.

## Block List checklist

Before finishing Block List work:

```text
[ ] work is on a feature branch/worktree, not main or dev
[ ] AGENTS.md and .frontis/* were read this session
[ ] block-design questions answered (settings, compositions, images, layouts)
[ ] spec written under docs/superpowers/specs/ and agreed
[ ] block document type sits in its own correctly named folder inside Block List
[ ] element types live in _Elements; layout element types live in the block's Layouts folder
[ ] custom Block List data types live in the Frontis data types folder
[ ] data types named "{property type} - {block/doc type} - {Settings|empty} - {field name}"
[ ] no description set on any created document type
[ ] "Vary by culture" turned off on every new Block List content and settings document type (regardless of number of cultures)
[ ] composition group ordered at the top; single Content tab (no stray Generic tab)
[ ] new fields have a higher sortOrder than the composition properties (compositions stay on top)
[ ] settings block / bg / width only created when confirmed
[ ] image link/aspect handled via compositions
[ ] layouts named Layout 1..N with blockList{Name}LayoutN properties; view branches on ModelTypeAlias
[ ] thumbnail created and selected on the block in the Block List data type; named {block-name-lowercase-with-dashes}.jpg, with -layout2..N for extra layouts
[ ] block added to the Block List data type, the content Block List and the rich-text link
[ ] preview page created under the Block List page (type content, named after the block)
[ ] preview page hidden: umbracoNaviHide, excludeFromSearchEngines and excludeFromInternalSearch all true
[ ] preview page shows the block with dummy data (one content block per layout)
[ ] pages under Block List sorted alphabetically; link added to the Block List text element in the right alphabetical spot
[ ] ViewComponent, view and (optional) view model follow BlockList{Name} naming
[ ] uSync files and regenerated models committed together
```
