01 — Umbraco Content Modeling
Dev Guide — Savoy Signature Hotels
PRD refs:06_Content_Modeling_Umbraco.md,03_MultiSite_and_Domains.md
1. Overview
Section titled “1. Overview”This guide defines how to create and maintain content types in Umbraco 17. The content model follows a strict hierarchy: Document Types (pages), Element Types (reusable modules), and Compositions (mixins). All content types are created in the Umbraco backoffice and must follow the naming and aliasing conventions below.
2. Content Type Categories
Section titled “2. Content Type Categories”3. Naming Conventions
Section titled “3. Naming Conventions”| Entity | Format | Example |
|---|---|---|
| Document Type name | PascalCase (display) | Room Detail Page |
| Document Type alias | camelCase | roomDetailPage |
| Element Type name | PascalCase (display) | Hero Slider |
| Element Type alias | camelCase | heroSlider |
| Composition name | PascalCase + “Composition” | SEO Composition |
| Composition alias | camelCase + “Composition” | seoComposition |
| Property alias | camelCase | metaTitle, imageDesktop |
| Property group/tab | Title Case | SEO, Content, Settings |
Rules:
- Aliases are immutable once content exists — changing them breaks API responses
- Always use camelCase for aliases (Umbraco convention, maps directly to JSON property names)
- Never use special characters or spaces in aliases
4. Compositions (Mixins)
Section titled “4. Compositions (Mixins)”Compositions are reusable property groups applied to multiple Document Types. Define them FIRST before creating Document Types.
4.1 seoComposition
Section titled “4.1 seoComposition”| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| Meta Title | metaTitle | Textstring | No | SEO |
| Meta Description | metaDescription | Textarea | No | SEO |
| Meta Keywords | metaKeywords | Tags | No | SEO |
| No Index | noIndex | Toggle | No | SEO |
| No Follow | noFollow | Toggle | No | SEO |
| Canonical URL | canonicalUrl | Textstring | No | SEO |
4.2 openGraphComposition
Section titled “4.2 openGraphComposition”| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| OG Title | ogTitle | Textstring | No | Open Graph |
| OG Description | ogDescription | Textarea | No | Open Graph |
| OG Image | ogImage | Media Picker | No | Open Graph |
| OG Type | ogType | Dropdown (website, article) | No | Open Graph |
| Twitter Card Type | twitterCardType | Dropdown (summary, summary_large_image) | No | Open Graph |
4.3 navigationComposition
Section titled “4.3 navigationComposition”| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| Hide in Nav | hideInNav | Toggle | No | Navigation |
| Nav Title | navTitle | Textstring | No | Navigation |
| Nav Order | navOrder | Numeric | No | Navigation |
4.4 responsiveImageComposition
Section titled “4.4 responsiveImageComposition”| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| Image Desktop | imageDesktop | Media Picker | Yes | Images |
| Image Mobile | imageMobile | Media Picker | Yes | Images |
Rule: Every image field in the system MUST follow the imageDesktop + imageMobile pattern. The frontend uses <picture> with <source> elements to serve the correct variant.
4.5 modulesComposition
Section titled “4.5 modulesComposition”| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| Modules | modules | Block List | No | Content |
5. Document Types (Pages)
Section titled “5. Document Types (Pages)”5.1 siteRoot
Section titled “5.1 siteRoot”The top-level node for each hotel site. One per hotel (8 total).
| Property | Alias | Editor | Required | Tab |
|---|---|---|---|---|
| Site Name | siteName | Textstring | Yes | Settings |
| Site Key | siteKey | Textstring | Yes | Settings |
| Theme | theme | Textstring | Yes | Settings |
| Logo | logo | Media Picker | Yes | Branding |
| Logo Alt (light) | logoAlt | Media Picker | No | Branding |
| Favicon | favicon | Media Picker | Yes | Branding |
| Default Meta Title | defaultMetaTitle | Textstring | Yes | SEO |
| Default Meta Description | defaultMetaDescription | Textarea | Yes | SEO |
| Default OG Image | defaultOgImage | Media Picker | Yes | SEO |
| Synxis Hotel ID | synxisHotelId | Textstring | No | Booking |
| Synxis Chain ID | synxisChainId | Textstring | Yes | Booking |
| Navarino Hotel Code | navarinoHotelCode | Textstring | No | Booking |
| Navarino API Token | navarinoApiToken | Textstring | No | Booking |
| GTM Container ID | gtmContainerId | Textstring | No | Analytics |
| Header Navigation | headerNavigation | Content Picker (Multiple) | Yes | Navigation |
| Footer Configuration | footerConfig | Block List | Yes | Navigation |
| Social Media Links | socialLinks | Block List | No | Navigation |
| Cookie Policy Page | cookiePolicyPage | Content Picker | Yes | Legal |
| Privacy Policy Page | privacyPolicyPage | Content Picker | Yes | Legal |
Allowed Children: homePage only.
5.2 homePage
Section titled “5.2 homePage”- Compositions:
seoComposition,openGraphComposition - Properties:
modules(Block List) - Allowed Children:
contentPage,roomsListPage,diningListPage,wellnessPage,experiencesPage,galleryPage,eventsPage,celebrationsPage,contactPage,newsListPage,specialOffersPage,faqPage
5.3 contentPage
Section titled “5.3 contentPage”- Compositions:
seoComposition,openGraphComposition,navigationComposition - Properties:
modules(Block List) - Allowed Children:
contentPage(nesting allowed)
5.4 roomsListPage / roomDetailPage
Section titled “5.4 roomsListPage / roomDetailPage”roomsListPage: title (Textstring), introduction (Rich Text), filterByCategory (Toggle). Children: roomDetailPage.
roomDetailPage:
- Compositions:
seoComposition,openGraphComposition,responsiveImageComposition
| Property | Alias | Editor | Required |
|---|---|---|---|
| Room Name | roomName | Textstring | Yes |
| Room Category | roomCategory | Dropdown (Room, Suite, Villa) | Yes |
| Short Description | shortDescription | Textarea | Yes |
| Full Description | fullDescription | Rich Text | Yes |
| Gallery | gallery | Block List of responsiveImageItem | No |
| Size (m2) | size | Numeric | No |
| Max Guests | maxGuests | Numeric | No |
| View | view | Textstring | No |
| Amenities | amenities | Block List | No |
| Floor Plan | floorPlan | Media Picker | No |
| Booking CTA | bookingCta | Toggle (default: true) | No |
| Modules | modules | Block List | No |
5.5 diningListPage / diningDetailPage
Section titled “5.5 diningListPage / diningDetailPage”diningDetailPage:
- Compositions:
seoComposition,openGraphComposition
| Property | Alias | Editor | Required |
|---|---|---|---|
| Restaurant Name | restaurantName | Textstring | Yes |
| Cuisine Type | cuisineType | Textstring | No |
| Short Description | shortDescription | Textarea | Yes |
| Full Description | fullDescription | Rich Text | Yes |
| Gallery | gallery | Block List of responsiveImageItem | No |
| Menu PDF | menuPdf | Media Picker | No |
| Opening Hours | openingHours | Block List | No |
| Dress Code | dressCode | Textstring | No |
| Reservations Required | reservationsRequired | Toggle | No |
| Location | location | Textstring | No |
| Modules | modules | Block List | No |
6. Element Types (Modules)
Section titled “6. Element Types (Modules)”Element Types represent the 25 content modules (M01-M25). They are used inside Block Lists on page Document Types.
6.1 Complete Module Registry
Section titled “6.1 Complete Module Registry”| ID | Alias | Display Name |
|---|---|---|
| M01 | headerModule | Header |
| M02 | footerModule | Footer |
| M03 | bookingBar | Booking Bar |
| M04 | pageHero | Page Hero |
| M05 | heroSlider | Hero Slider |
| M06 | richTextBlock | Rich Text Block |
| M07 | imageTextBlock | Image + Text |
| M08 | cardGrid | Card Grid |
| M09 | featuredCards | Featured Cards |
| M10 | testimonials | Testimonials |
| M11 | imageGallery | Image Gallery |
| M12 | videoBlock | Video Block |
| M13 | accordion | Accordion |
| M14 | tabsBlock | Tabs Block |
| M15 | ctaBanner | CTA Banner |
| M16 | mapBlock | Map Block |
| M17 | formModule | Form Module |
| M18 | hotelListBlock | Hotel List |
| M19 | offersCarousel | Offers Carousel |
| M20 | statsBlock | Stats Block |
| M21 | timelineBlock | Timeline Block |
| M22 | teamBlock | Team Block |
| M23 | newsletterSignup | Newsletter Signup |
| M24 | socialFeed | Social Feed |
| M25 | downloadsBlock | Downloads Block |
6.2 Example: heroSlider (M05)
Section titled “6.2 Example: heroSlider (M05)”| Property | Alias | Editor | Required |
|---|---|---|---|
| Title | title | Block List (htmlHeading, single-block mode) | No |
| Subtitle | subtitle | Block List (htmlHeading, single-block mode) | No |
| Slides | slides | Block List of heroSlide | Yes |
| Autoplay | autoplay | Toggle (default: true) | No |
| Interval | interval | Numeric (default: 5000) | No |
Note: Title and Subtitle use the reusable
htmlHeadingElement Type (see section 6.4). This allows editors to control both the visible text (with bold formatting) and the semantic HTML tag (h1-h6,span,p).
heroSlide (nested Element Type):
| Property | Alias | Editor | Required |
|---|---|---|---|
| Image Desktop | imageDesktop | Media Picker | Yes |
| Image Mobile | imageMobile | Media Picker | Yes |
| Caption | caption | Textstring | No |
| CTA Label | ctaLabel | Textstring | No |
| CTA Link | ctaLink | URL Picker | No |
6.3 Example: bookingBar (M03)
Section titled “6.3 Example: bookingBar (M03)”| Property | Alias | Editor | Required |
|---|---|---|---|
| Style Variant | styleVariant | Dropdown (inline, overlay, sticky) | Yes |
| Show Room Filter | showRoomFilter | Toggle | No |
| Promo Code | promoCode | Textstring | No |
Reads synxisHotelId, synxisChainId, navarinoHotelCode, navarinoApiToken from the siteRoot ancestor.
6.4 htmlHeading (Reusable Heading Element)
Section titled “6.4 htmlHeading (Reusable Heading Element)”Used for ALL title, subtitle, and label/eyebrow fields across modules. Provides editor control over both the visible text (with bold formatting via RTE) and the semantic HTML tag.
| Property | Alias | Editor | Required | Varies by Culture |
|---|---|---|---|---|
| Text | text | Rich Text (Tiptap, bold-only toolbar) | Yes | Yes |
| HTML Tag | html | Dropdown (h1, h2, h3, h4, h5, h6, span, p) | Yes | No (invariant) |
Block List configuration: Use as a single-block Block List (useSingleBlockMode: true, validationLimit.max = 1) on title/subtitle/label properties. This renders the heading inline in the editor, not as a list.
Rule: Never use a plain Textstring for heading fields. Always use htmlHeading via Block List so editors can control both text styling and semantic tag.
6.5 responsiveImageItem (Reusable Nested Element)
Section titled “6.5 responsiveImageItem (Reusable Nested Element)”Used in galleries across multiple modules:
| Property | Alias | Editor | Required |
|---|---|---|---|
| Image Desktop | imageDesktop | Media Picker | Yes |
| Image Mobile | imageMobile | Media Picker | Yes |
| Alt Text | altText | Textstring | Yes |
| Caption | caption | Textstring | No |
7. Block List Configuration
Section titled “7. Block List Configuration”Block Lists are the primary mechanism for composing modules on pages.
Configuration Steps:
- Create the Element Type (e.g.,
heroSlider) - Go to the page Document Type (e.g.,
homePage) - Open the
modulesproperty configuration - Add the Element Type as an allowed block
- Configure label template (e.g.,
{{title}}orHero Slider) - Set min/max items if needed
- Enable/disable inline editing as appropriate
Allowed Blocks per Page Type:
| Page Type | Allowed Modules |
|---|---|
homePage | M04-M25 (all except Header/Footer/Booking Bar — those are global) |
contentPage | M04, M06-M17, M20-M25 |
roomDetailPage | M06, M07, M08, M11, M13, M15 |
diningDetailPage | M06, M07, M11, M13, M15 |
8. Multi-Language Properties
Section titled “8. Multi-Language Properties”Properties that vary by culture (must be translated):
- All text content (titles, descriptions, labels)
- SEO fields (meta title, meta description)
- URL slugs
- Media fields (when images contain visible text)
Properties that are invariant (same across languages):
- Numeric values (size, maxGuests, interval)
- Toggles (autoplay, noIndex, bookingCta)
- Content Pickers
- Configuration fields (theme, siteKey, synxisHotelId)
9. Step-by-Step: Creating a New Element Type
Section titled “9. Step-by-Step: Creating a New Element Type”- Define the schema — List all properties, aliases, editors, and required flags
- Create in Umbraco backoffice — Settings > Document Types > Element Types > Create
- Set the alias — Use camelCase, matching the FE module registry key
- Add properties — Follow the table schema; group into tabs (Content, Settings, Images)
- Apply compositions — Add
responsiveImageCompositionif images are needed - Configure Block List — Add as allowed block on target page Document Types
- Notify FE team — Confirm alias and property names match the
.types.tsinterface - Test API output — Fetch a page with the block via Content Delivery API, verify JSON shape
10. Common Pitfalls
Section titled “10. Common Pitfalls”| Pitfall | Solution |
|---|---|
| Changed an alias after content exists | Aliases are immutable. Create a new property + migrate data |
Missing imageMobile field | Every image MUST have desktop + mobile variants |
| Used Textstring for title/subtitle/label | Always use htmlHeading Element Type via Block List (single-block mode) |
| Forgot to set “vary by culture” | Text properties must vary by culture for multi-language support |
| Block List not configured on page type | Add the Element Type as an allowed block on the target Document Type |
| Nested Block Lists too deep | Max 2 levels deep (page > module > nested element) |
| Property name doesn’t match FE type | Coordinate aliases with packages/cms-client/src/types.ts |