Skip to content

01 — Umbraco Content Modeling

Dev Guide — Savoy Signature Hotels
PRD refs: 06_Content_Modeling_Umbraco.md, 03_MultiSite_and_Domains.md


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.


Content Types

Document Types (pages with URLs)

siteRoot, homePage, contentPage, roomDetailPage, etc.

Element Types (modules, no URLs)

heroSlider, cardGrid, bookingBar, etc.

Compositions (mixins, shared property groups)

seoComposition, openGraphComposition, navigationComposition, etc.


EntityFormatExample
Document Type namePascalCase (display)Room Detail Page
Document Type aliascamelCaseroomDetailPage
Element Type namePascalCase (display)Hero Slider
Element Type aliascamelCaseheroSlider
Composition namePascalCase + “Composition”SEO Composition
Composition aliascamelCase + “Composition”seoComposition
Property aliascamelCasemetaTitle, imageDesktop
Property group/tabTitle CaseSEO, 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

Compositions are reusable property groups applied to multiple Document Types. Define them FIRST before creating Document Types.

PropertyAliasEditorRequiredTab
Meta TitlemetaTitleTextstringNoSEO
Meta DescriptionmetaDescriptionTextareaNoSEO
Meta KeywordsmetaKeywordsTagsNoSEO
No IndexnoIndexToggleNoSEO
No FollownoFollowToggleNoSEO
Canonical URLcanonicalUrlTextstringNoSEO
PropertyAliasEditorRequiredTab
OG TitleogTitleTextstringNoOpen Graph
OG DescriptionogDescriptionTextareaNoOpen Graph
OG ImageogImageMedia PickerNoOpen Graph
OG TypeogTypeDropdown (website, article)NoOpen Graph
Twitter Card TypetwitterCardTypeDropdown (summary, summary_large_image)NoOpen Graph
PropertyAliasEditorRequiredTab
Hide in NavhideInNavToggleNoNavigation
Nav TitlenavTitleTextstringNoNavigation
Nav OrdernavOrderNumericNoNavigation
PropertyAliasEditorRequiredTab
Image DesktopimageDesktopMedia PickerYesImages
Image MobileimageMobileMedia PickerYesImages

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.

PropertyAliasEditorRequiredTab
ModulesmodulesBlock ListNoContent

The top-level node for each hotel site. One per hotel (8 total).

PropertyAliasEditorRequiredTab
Site NamesiteNameTextstringYesSettings
Site KeysiteKeyTextstringYesSettings
ThemethemeTextstringYesSettings
LogologoMedia PickerYesBranding
Logo Alt (light)logoAltMedia PickerNoBranding
FaviconfaviconMedia PickerYesBranding
Default Meta TitledefaultMetaTitleTextstringYesSEO
Default Meta DescriptiondefaultMetaDescriptionTextareaYesSEO
Default OG ImagedefaultOgImageMedia PickerYesSEO
Synxis Hotel IDsynxisHotelIdTextstringNoBooking
Synxis Chain IDsynxisChainIdTextstringYesBooking
Navarino Hotel CodenavarinoHotelCodeTextstringNoBooking
Navarino API TokennavarinoApiTokenTextstringNoBooking
GTM Container IDgtmContainerIdTextstringNoAnalytics
Header NavigationheaderNavigationContent Picker (Multiple)YesNavigation
Footer ConfigurationfooterConfigBlock ListYesNavigation
Social Media LinkssocialLinksBlock ListNoNavigation
Cookie Policy PagecookiePolicyPageContent PickerYesLegal
Privacy Policy PageprivacyPolicyPageContent PickerYesLegal

Allowed Children: homePage only.

  • Compositions: seoComposition, openGraphComposition
  • Properties: modules (Block List)
  • Allowed Children: contentPage, roomsListPage, diningListPage, wellnessPage, experiencesPage, galleryPage, eventsPage, celebrationsPage, contactPage, newsListPage, specialOffersPage, faqPage
  • Compositions: seoComposition, openGraphComposition, navigationComposition
  • Properties: modules (Block List)
  • Allowed Children: contentPage (nesting allowed)

roomsListPage: title (Textstring), introduction (Rich Text), filterByCategory (Toggle). Children: roomDetailPage.

roomDetailPage:

  • Compositions: seoComposition, openGraphComposition, responsiveImageComposition
PropertyAliasEditorRequired
Room NameroomNameTextstringYes
Room CategoryroomCategoryDropdown (Room, Suite, Villa)Yes
Short DescriptionshortDescriptionTextareaYes
Full DescriptionfullDescriptionRich TextYes
GallerygalleryBlock List of responsiveImageItemNo
Size (m2)sizeNumericNo
Max GuestsmaxGuestsNumericNo
ViewviewTextstringNo
AmenitiesamenitiesBlock ListNo
Floor PlanfloorPlanMedia PickerNo
Booking CTAbookingCtaToggle (default: true)No
ModulesmodulesBlock ListNo

diningDetailPage:

  • Compositions: seoComposition, openGraphComposition
PropertyAliasEditorRequired
Restaurant NamerestaurantNameTextstringYes
Cuisine TypecuisineTypeTextstringNo
Short DescriptionshortDescriptionTextareaYes
Full DescriptionfullDescriptionRich TextYes
GallerygalleryBlock List of responsiveImageItemNo
Menu PDFmenuPdfMedia PickerNo
Opening HoursopeningHoursBlock ListNo
Dress CodedressCodeTextstringNo
Reservations RequiredreservationsRequiredToggleNo
LocationlocationTextstringNo
ModulesmodulesBlock ListNo

Element Types represent the 25 content modules (M01-M25). They are used inside Block Lists on page Document Types.

IDAliasDisplay Name
M01headerModuleHeader
M02footerModuleFooter
M03bookingBarBooking Bar
M04pageHeroPage Hero
M05heroSliderHero Slider
M06richTextBlockRich Text Block
M07imageTextBlockImage + Text
M08cardGridCard Grid
M09featuredCardsFeatured Cards
M10testimonialsTestimonials
M11imageGalleryImage Gallery
M12videoBlockVideo Block
M13accordionAccordion
M14tabsBlockTabs Block
M15ctaBannerCTA Banner
M16mapBlockMap Block
M17formModuleForm Module
M18hotelListBlockHotel List
M19offersCarouselOffers Carousel
M20statsBlockStats Block
M21timelineBlockTimeline Block
M22teamBlockTeam Block
M23newsletterSignupNewsletter Signup
M24socialFeedSocial Feed
M25downloadsBlockDownloads Block
PropertyAliasEditorRequired
TitletitleBlock List (htmlHeading, single-block mode)No
SubtitlesubtitleBlock List (htmlHeading, single-block mode)No
SlidesslidesBlock List of heroSlideYes
AutoplayautoplayToggle (default: true)No
IntervalintervalNumeric (default: 5000)No

Note: Title and Subtitle use the reusable htmlHeading Element 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):

PropertyAliasEditorRequired
Image DesktopimageDesktopMedia PickerYes
Image MobileimageMobileMedia PickerYes
CaptioncaptionTextstringNo
CTA LabelctaLabelTextstringNo
CTA LinkctaLinkURL PickerNo
PropertyAliasEditorRequired
Style VariantstyleVariantDropdown (inline, overlay, sticky)Yes
Show Room FiltershowRoomFilterToggleNo
Promo CodepromoCodeTextstringNo

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.

PropertyAliasEditorRequiredVaries by Culture
TexttextRich Text (Tiptap, bold-only toolbar)YesYes
HTML TaghtmlDropdown (h1, h2, h3, h4, h5, h6, span, p)YesNo (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:

PropertyAliasEditorRequired
Image DesktopimageDesktopMedia PickerYes
Image MobileimageMobileMedia PickerYes
Alt TextaltTextTextstringYes
CaptioncaptionTextstringNo

Block Lists are the primary mechanism for composing modules on pages.

Configuration Steps:

  1. Create the Element Type (e.g., heroSlider)
  2. Go to the page Document Type (e.g., homePage)
  3. Open the modules property configuration
  4. Add the Element Type as an allowed block
  5. Configure label template (e.g., {{title}} or Hero Slider)
  6. Set min/max items if needed
  7. Enable/disable inline editing as appropriate

Allowed Blocks per Page Type:

Page TypeAllowed Modules
homePageM04-M25 (all except Header/Footer/Booking Bar — those are global)
contentPageM04, M06-M17, M20-M25
roomDetailPageM06, M07, M08, M11, M13, M15
diningDetailPageM06, M07, M11, M13, M15

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”
  1. Define the schema — List all properties, aliases, editors, and required flags
  2. Create in Umbraco backoffice — Settings > Document Types > Element Types > Create
  3. Set the alias — Use camelCase, matching the FE module registry key
  4. Add properties — Follow the table schema; group into tabs (Content, Settings, Images)
  5. Apply compositions — Add responsiveImageComposition if images are needed
  6. Configure Block List — Add as allowed block on target page Document Types
  7. Notify FE team — Confirm alias and property names match the .types.ts interface
  8. Test API output — Fetch a page with the block via Content Delivery API, verify JSON shape

PitfallSolution
Changed an alias after content existsAliases are immutable. Create a new property + migrate data
Missing imageMobile fieldEvery image MUST have desktop + mobile variants
Used Textstring for title/subtitle/labelAlways 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 typeAdd the Element Type as an allowed block on the target Document Type
Nested Block Lists too deepMax 2 levels deep (page > module > nested element)
Property name doesn’t match FE typeCoordinate aliases with packages/cms-client/src/types.ts