HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Advanced content types

Configure advanced content types in Optimizely CMS 13. Covers experience, section, Connect from Graph, contract, and orphaned types, plus typed content references for developers.

📘

Note

This topic is for CMS administrators and developers with administrative access rights.

Advanced content types extend the standard page, block, and media types for specialized patterns. Use them for Visual Builder layouts, external GraphQL sources, shared structural contracts, and typed code references. This article addresses developers and administrators with code-level access. Editing advanced content types requires CMS administrator permissions.

For standard page, block, and media types, see Content types in the UI.

Contract types

Contracts define a shared structure (a set of required properties) that multiple content types can implement. When you configure a Content Reference or ContentReference<T> property and select an allowed reference type, the type picker filters contracts from the list by default. Only concrete content types appear as selectable reference targets.

To reference content by contract rather than by a concrete type, configure the property's Reference Type as a specific contract. The content picker then surfaces all content types implementing that contract.

See Create contracts.

Experience types

An Experience content type extends the page type for use with the Visual Builder, letting editors compose flexible page layouts from sections. Define experience types to give content authors reusable, structured canvases for campaigns, landing pages, and similar work.

To create an experience type from the Settings view:

  1. Go to Settings > Content Types.

  2. Select Create New > Experience Type. The Create New Experience Type window displays.

  3. Complete the following fields:

    • Name (required) – Enter a unique identifier for the content type (for example, LoremIpsum). This is the internal key used by the system.
    • Display name – Enter a human-readable label that editors see in the interface (for example, Lorem ipsum).
    • Description – Optionally, add a description to clarify the purpose of this content type for your team
  4. Click Create to save the Experience content type.

    📘

    Note

    Experience types include a built-in Blank Experience managed by Optimizely. While you cannot adjust its settings, you can modify Permissions and Properties on any custom experience type you create. Once created, you can add properties to define the data model for your experience.

Section types

Section content types organize content within an Experience type in the Visual Builder, giving editors reusable building blocks for layout regions. Define custom section types to expose domain-specific properties such as photographer or copyright.

Section types include a built-in Blank Section managed by Optimizely. To create a custom section type from the Settings view:

  1. Go to Settings > Content Types

  2. Select Create New > Section Type. The Create New Section window displays.

  3. Complete the following fields:

    • Name (required) – Enter a unique identifier for the content type (for example, MaurisEleifend). This is the internal key used by the system.
    • Display name – Enter a human-readable label that editors see in the interface (for example, Mauris eleifend).
    • Description – Optionally, add a description to clarify the purpose of this content type for your team (for example, describing the custom properties it exposes, such as Copyright or Photographer).
  4. Click Create to save the Section content type.

    📘

    Note

    Once created, you can modify Settings, Permissions, and Properties on any custom Section type. Use properties to define additional metadata that editors can populate when working within the Visual Builder.

Connect from Graph types

Connect from Graph lets you create a connected content source by pointing the CMS at an external GraphQL endpoint. This allows external data (such as products from a PIM, assets from a DAM, or records from any GraphQL-compatible API) to appear as content types within Optimizely CMS without a full data sync.

To create a connected content source:

  1. Go to Settings > Content Types and select Create New > Connect From Graph.

  2. Configure the following in the Connect From Graph dialog:

    • Name – An internal identifier for the connected content source.
    • Display name – The label shown in the CMS UI.
    • GraphQL endpoint URL – The URL of the external GraphQL API to connect to.
    • Authentication – Provide any required headers or tokens needed to authenticate requests to the external endpoint.
  3. Connect to the endpoint; the CMS then introspects the external schema and lists the available content types from the external source.

  4. Select the content types you want to expose in the CMS and map their properties to CMS property types.

  5. Click Save.

The following list shows the behavior of connected content types:

  • Connected content types display alongside native CMS content types in the Content Types list, with a distinct Connected indicator.
  • Editors can select and reference connected content in Content Reference and ContentReference<T> properties, provided the connected type is included in the allowed types for that property.
  • Properties populated from the external source are read-only in the CMS Edit UI.
  • If the connected content type implements a global contract, administration settings are restricted: only the External edit URL is editable.

For information on mapping connected content type properties, see Connect content sources using GraphQL.

Orphaned content types

When a NuGet package that registered one or more content types is removed from a project without first deleting those content types from the CMS, the types become orphaned. At runtime, the CMS cannot resolve the .NET type backing the content type, which causes an EPiServerException similar to the following:

EPiServer.Core.EPiServerException: No type mapped for content type ID [id].
   at EPiServer.DataAbstraction.RuntimeModel.ContentTypeModelRepository.TransformModel(ContentTypeModel model)

Orphaned content types also display an Undefined label in the Base type column of the Content Types list.

Resolution strategies

Resolution strategies fix the runtime exception caused by orphaned content types and protect existing content from data loss. Pick the option that matches the environment and the project's tolerance for downtime.

Choose one of the following approaches to resolve orphaned content types:

Option 1: Delete from the Admin UI (recommended for non-production)

  1. Go to Settings > Content Types.
  2. Filter by Base type: Undefined to locate orphaned types.
  3. Select the orphaned content type and click Delete.
❗️

Warning

Deleting a content type permanently removes all content instances based on that type. Verify that no live content depends on the type before deleting.

Option 2: Add stub classes (recommended for production)

Create a minimal stub class in your codebase that matches the original type registration. This resolves the runtime exception without data loss and gives you time to migrate or remove content safely.

using EPiServer.Core;
using EPiServer.DataAnnotations;

[ContentType(GUID = "your-original-guid-here", AvailableInEditMode = false)]
public class OrphanedBlockStub : BlockData
{
    // No properties needed: stub only
}

After deploying the stub:

  1. Migrate any existing content instances away from the orphaned type.
  2. Remove the stub class and delete the content type from the Admin UI once no content instances remain.

Option 3: Database cleanup (advanced)

If neither of the previous options is viable, a database administrator can remove the orphaned content type record directly from the tblContentType table. Perform this operation only in a non-production environment first, and always take a full database backup before proceeding.

Typed content references

ContentReference<T> is a strongly typed variant of the standard Content Reference property. Specifying the type parameter T restricts the property to accept only content of that specific type, improving type safety and enabling richer Optimizely Graph responses.

When you configure a Content Reference property with a specific type, other than Any, the CMS:

  • Limits the content picker to content matching the declared type T.
  • Returns the full typed object in Optimizely Graph. For example, a property typed to ContactBlock returns all fields such as name and email, rather than only contentLink.
  • Filters content type contracts out of the type picker by default. To type a reference against a contract, explicitly select the contract as the Reference Type.

The following table shows Reference Type and Optimizely Graph behavior:

Reference type configurationOptimizely Graph returns
Specific concrete type (for example, ContactBlock)Full typed object with all fields
Contract (for example, IProductContract)Full object for all implementing types
Any + Allowed Content TypescontentLink only

Configure a typed reference from the admin view

  1. Go to Settings > Content Types and select the content type.
  2. Select the property, then open its Configure Property panel.
  3. Set Reference Type to a specific content type (not Any) and select the target type or contract from the list.
  4. Save the content type. When you select a contract as the reference type, the content picker in the editor surfaces all content types that implement that contract.

Define a typed reference in code

// Restricts selection to ContactBlock instances only
[Display(Name = "Contact", Order = 100)]
public virtual ContentReference<ContactBlock> Contact { get; set; }

// Restricts selection to any content type implementing IProductContract
[Display(Name = "Featured Product", Order = 110)]
public virtual ContentReference<IProductContract> FeaturedProduct { get; set; }