Robert Svallin
Mar 20, 2026
  66
(1 votes)

CMS 13 Preview 4 — Upgrading from Preview 3

This is the third post in a series where I use the Alloy template as a reference to walk through each CMS 13 preview. The first post covered upgrading from CMS 12 to Preview 2, and the second looked at the key changes in Preview 3. If you're following along, pick up where we left off.

Preview 4 is out. The package bump is easy enough, but there's an API change around how you resolve the start page that'll ripple through your code. Here's what to change and what's worth knowing about.

Step 1: Update Packages

Bump your package references from preview3 to preview4:

<PackageReference Include="EPiServer.CMS" Version="13.0.0-preview4" />
<PackageReference Include="EPiServer.CMS.UI.AspNetIdentity" Version="13.0.0-preview4" />
<PackageReference Include="Optimizely.Graph.Cms" Version="13.0.0-preview4" />

Known Issue: Database Schema Upgrade from Older CMS 12 Versions

If you're upgrading directly from an older CMS 12 version (e.g. 12.34.1 or earlier) to a CMS 13 preview and not from preview3 (which this article assumes), the database schema migration may fail with an error like:
Column names in each table must be unique. Column name 'Failed' in table 'dbo.tblNotificationMessage' is specified more than once.
This happens because certain columns were added in later CMS 12 releases, and the CMS 13 migration scripts don't check whether they already exist before trying to add them.
Workaround: First upgrade to the latest CMS 12 version so that your database schema is fully up to date, then upgrade from there to the CMS 13 preview. This ensures all intermediate schema changes are applied before the CMS 13 migration runs.

Step 2: Resolving the Start Page

In Preview 3, Alloy template resolved the start page by casting to Website and accessing RoutingEntryPoint. In Preview 4, the simplest approach is to use ContentReference.StartPage directly — no need to go through IApplicationResolver in most cases.

Before (Preview 3)

var website = _applicationResolver.GetByContext() as Website;
var startPageContentLink = website?.RoutingEntryPoint;

After (Preview 4)

var startPageContentLink = ContentReference.StartPage;

In the Alloy template this simplified things across several files by using ContentReference.StartPage 
- PageViewContextFactory.cs — CreateLayoutModel 
ContentLocator.cs — GetContactPages 
- PreviewController.cs — the Index action
- StartPageController.cs — the Index action
Breadcrumbs.cshtml and Header.cshtml — same change

If you do need more control, IRoutableApplication with its EntryPoint property is still available as an alternative.

The SiteDefinition migration keeps getting refined. Issues with default application provisioning and hostname resolution between preview and view modes from earlier previews have been sorted out.

Step 3: DAM Integration (optional)

Preview 4 adds a new package for Optimizely DAM integration. Its important to call out that even though DAM is a fundamental piece of CMS 13 it is possible to run without it. CMS still can store assets in te traditional sense.

Similar to CMS 12 the DAM configuration is handled using options directly requiring an application to be created in CMP from which the credentials can be retrieved for configuration in CMS. You'll also need the SSO ID which can be found under Settings \ Organization \ General in CMP.

The DAM integration no longer talks to the CMP REST API directly. Instead it's built on External Sources, which means DAM assets need to be indexed into the Optimizely Graph instance connected to your CMS. So before wiring up the code below, make sure you have Optimizely Graph set up, then contact Optimizely Support to connect DAM to your Graph instance. The onboarding steps — selecting your DAM instance, activating asset types — follow the same process as CMS SaaS and are documented in the Onboard DAM to CMS guide. Updated documentation specific to CMS 13 will be available at release. Once that's done, Content Manager can be used to discover and pick DAM assets directly from the editing UI.

Add the package:

<PackageReference Include="EPiServer.Cms.DamIntegration.UI" Version="13.0.0-preview4" />

Register the DAM UI in Startup.cs:

services.AddDamUI();

Add the DAM HTML helpers namespace to Views/_ViewImports.cshtml:

@using EPiServer.Cms.DamIntegration.UI.Helpers

This gives you access to helpers like RenderTagWithMetadata(...) for rendering DAM assets. Note that the epi-property tag helper you're already using comes from EPiServer.Cms.AspNetCore.TagHelpers, which should already be registered in your _ViewImports.cshtml via @addTagHelper.

Then configure your CMP credentials (note that these have defaults, included here for transparency) in appsettings.json:

"Optimizely": {
  "Cms": {
    "DamUI": {
      "Endpoint": "https://cmp.optimizely.com",
      "SsoId": "<your-sso-id>",
      "NavigationUrl": "https://cmp.optimizely.com/cloud/library"
    }
  },
  "Cmp": {
    "Client": {
      "TokenUrl": "https://accounts.cmp.optimizely.com/o/oauth2/v1/token",
      "ApiUrl": "https://api.cmp.optimizely.com/v3/",
      "ClientId": "<your-client-id>",
      "ClientSecret": "<your-client-secret>"
    }
  }
}

The DAM picker now also supports multi-select, which is a nice improvement for editors working with lots of media.

Migrating from CMS 12 DAM Integration

For the GA release of CMS 13, a standalone migration package will be available that converts the CMS 12-style DAM integration to the new one. This package will handle the migration of existing DAM asset references so that they work with the new EPiServer.Cms.DamIntegration.UI package.

The first version of the migration package will support:
  - ContentReference properties
  - IList<ContentReference> properties

Support for additional property types will be added in future releases.

Using DAM Assets in Your Content

To use DAM assets on a page, add a ContentReference property with the UIHint.Image hint:

using EPiServer.Web;
using System.ComponentModel.DataAnnotations;

public class DamExamplePage : StandardPage
{
    [Display(GroupName = Globals.GroupNames.Content)]
    [UIHint(UIHint.Image)]
    public virtual ContentReference Image { get; set; }
}

In the view, render it with the epi-property tag helper — same pattern as any other CMS property:

<img epi-property="@Model.CurrentPage.Image" />

Step 4: Audiences (optional)

Audiences are a separate package in CMS 13. If you were using services.AddVisitorGroups() in Preview 3, you'll need to add the new NuGet package and update your service registration.

Add the package:

<PackageReference Include="EPiServer.Cms.UI.VisitorGroups" Version="13.0.0-preview4" />

Then update your Startup.cs — the old AddVisitorGroups() call is replaced with two separate registrations:

using EPiServer.Cms.UI.VisitorGroups;
services.AddVisitorGroupsMvc().AddVisitorGroupsUI();

A few audience-related bugs have also been fixed: the policy-not-found error is gone, Geographic Location criteria works, and context menu positioning for personalized groups has been corrected.

What Else Is New in Preview 4

A few things worth knowing about beyond the upgrade steps:

REST API — Always Included

The REST API is now always included in CMS 13 projects — no separate opt-in needed which includes **Application Management API** (experimental) — that lets you manage applications programmatically

Graph .NET SDK

There's a new .NET SDK for Optimizely Graph with a fluent API covering facets, caching, auth, tracking, and object-to-Graph field mapping. Extensibility points have also been added to ContentGraph.CMS so you can hook into the indexing pipeline.

Improved Migration from SiteDefinition

The migration from SiteDefinition to the Application Model has been improved. Creating in-process websites via the settings UI works again, and typed content types now show up correctly when picking the start page for an application.

For the full list of changes, check the official release notes.

Mar 20, 2026

Comments

Please login to comment.
Latest blogs
Integrating Searchspring with Optimizely – Part 1: Architecture & Setup

Integrating Searchspring with Optimizely – Part 1: Architecture & Setup

Wiselin Jaya Jos | Mar 20, 2026 |

The move to CMS 13: Upgrade Notes for Technical Teams

A technical walkthrough of CMS 13 preview3 and headless work: what is changing, where the risks are, and how an upgrade and what to expect

Hristo Bakalov | Mar 20, 2026 |

A Synonyms Manager for Optimizely Graph

If you’re using Optimizely Graph for search, synonyms are one of the simplest ways to improve relevance without touching content. But they’re also...

Pär Wissmark | Mar 17, 2026 |

Building a Better Link Validation Report in Optimizely CMS 12

Broken links frustrate visitors and damage SEO. I have made a custom broken links report, that makes it easier to work broken links than the built-...

Henning Sjørbotten | Mar 17, 2026 |

Jhoose Security Module V3.0.0 – Site-Level Security Configuration for Optimizely

Discover what's new in Jhoose Security Module 3.0, including site-level security configuration for multi-site Optimizely solutions with global...

Andrew Markham | Mar 15, 2026 |