Back to blog

Migrating large solutions to Optimizely CMS 13: the add-on ecosystem is catching up fast

Field notes from a live CMS 12 -> CMS 13 / .NET 10 upgrade. Everything below is my own read of public repositories, open PRs and the Optimizely NuGet feed as of 31 May 2026 — not official guidance, just what I’m seeing. These feeds move quickly, so re-check anything date-sensitive before you commit.

CMS 13 is generally available, it targets .NET 10 exclusively, and it ships an application-based architecture that changes how add-ons hook into the platform. The practical consequence for an existing solution is blunt: every package you depend on has to target .NET 10 and reference the 13.x assemblies before the site will start at all.

A week ago, when I last wrote this up, the third-party picture was thin: one package shipped, one close behind, and unanswered upgrade issues everywhere else. The conclusion then was that the add-ons, not the CMS core, were the critical path.

That conclusion still holds, but the underlying state has moved quickly. In the past week or two, several maintainers merged upgrade branches, opened and reviewed pull requests, and assigned 13.x milestones; for the one package that isn’t being migrated, the public direction now points at a native CMS 13 replacement. Most of this work lives in public branches and PRs you can read today.

Status at a glance

PackageMaintainerCMS 13 / .NET 10Where it stands
Stott.Optimizely.RobotsHandlerMark Stott✅ Shippedv7.0.0 on the Optimizely feed, pinned to CMS UI Core 13.x
GoogleMapsEditor (tedgustaf)Ted Gustaf / community✅ Shippedv5.0.0 published, requires CMS UI Core ≥ 13.0.0
Geta.Optimizely.SitemapsGeta🟡 Code-completePR #143 merged into the upgrade/cms13 branch (29 May)
Geta.NotFoundHandler.OptimizelyGeta🟡 In reviewPR #187 open, targets .NET 10 / CMS 13 / Commerce 15, v7.0 milestone
Geta.Optimizely.TagsGeta🟡 In progressIssue #32 active, on the 3.0 milestone
DbLocalizationProvider (.EPiServer)Valdis Iljuconoks🟡 In progresscms13 branch active, v9 line (some items may slip to 9.1)
Addon.Episerver.EnvironmentSynchronizerEpinova🟡 In reviewPR #30 open, .NET 10 / CMS 13 / application framework
EPiServer.Labs.LanguageManagerOptimizely (Labs)🟡 In progressOptimizely targets this quarter, likely around June
EPiServer.Personalization.MaxMindGeolocationOptimizely➡️ ReplacedNo migration planned; use native AddCmsClientGeolocation

Legend: ✅ available · 🟡 in flight · ➡️ superseded by a native capability.

That is two packages shipped, six with upgrade code you can read today, and one retired in favour of a built-in CMS 13 feature. A week ago, most of the in-flight row was open issues with no code attached. For anyone sequencing an upgrade, that shift from open issues to reviewable code is what makes planning possible.

Why CMS 13 breaks add-ons (and why a green build can mislead you)

For add-on authors, CMS 13 is not a routine version bump. Three source-level breaking changes account for almost every failure I’ve hit, and they share an awkward property: the package compiles cleanly and then fails at startup. Validate on a running CMS 13 instance, not on a successful dotnet build.

IContentRouteResolver gained a required member. Custom route resolvers, for example for language or segment routing, must now implement TryResolveUriAsync. A resolver compiled against CMS 12 throws a “does not have an implementation” error at runtime. That is the change that took down Language Manager.

PropertyLongString now derives from PropertyString. A property type built on the old base can fail with a TypeLoadException reporting that a derived get_String cannot reduce the parent’s accessibility. This is what broke Geta Sitemaps’ PropertySEOSitemaps; the merged PR fixes it by widening the String override’s accessibility for CMS 13.

LocalizationProvider.GetString has a new signature. This one is binary-breaking: a GetString override compiled against CMS 12 no longer satisfies the abstract member, so the type scanner throws a TypeLoadException at startup. It is the blocker for DbLocalizationProvider, and the reason that package needs a port rather than a recompile.

All three fixes are now visible in public PRs, so you can read the actual remediation rather than guess at the scope.

What a CMS 13 add-on upgrade involves

Reading the upgrade PRs as they land has been the most useful part of this migration. They describe the work more concretely than any changelog. If you maintain your own modules, expect to touch the same areas the package maintainers are touching:

  • Routing moves from site definitions to applications. The application model replaces much of the old SiteDefinition.Current-style discovery. NotFoundHandler’s upgrade, for example, moves its content-link provider to application-based content links and updates the tests accordingly.
  • Service location and static caches are out. Replace ServiceLocator and the old CacheManager with constructor-injected dependencies. ISynchronizedObjectInstanceCache is the direct replacement Sitemaps adopted.
  • Shell UI navigation uses tag helpers. The _ShellLayout.cshtml views are being rewritten around the platform-navigation tag helper instead of the previous markup.
  • Content Graph appears throughout. Sample apps are moving configuration from Find to Content Graph, and any add-on that talks to Graph needs a sensible path for when Graph isn’t configured. Sitemaps handles this with a no-op Graph sync client.
  • System.Text.Json replaces Newtonsoft.Json in several of these upgrades. It looks minor, but it will catch you out wherever you serialise anything custom.

Individually none of this is hard, but it accumulates. Budget real time for runtime testing of every custom property type, route resolver and scheduled job, because that is where the TypeLoadExceptions surface.

Ready today

Stott.Optimizely.RobotsHandler (v7.0.0). The cleanest case on the list. It targets .NET 10, constrains its CMS UI Core reference to [13.0.0,14.0.0), and the release notes describe it as a recompile for CMS 13. Mark Stott’s companion Stott Security package has a matching v7 line, so if you run both, you can move them in step. This one you can plan in now.

GoogleMapsEditor (tedgustaf, v5.0.0). Shipped since the last write-up. The community upgrade, originally PR #9 by jacobjones, took the add-on to v5.0.0, switched the target framework to net10.0, and moved every EPiServer 12.* reference to 13.0.0 or later. It also cleared a batch of CMS 13 and Visual Builder bugs: contentTypeGuid round-tripping in the text-block converter, new list items being dropped in Visual Builder, a textbox coercion issue, and some styling. The maintainer tested and published the result; v5.0.0 is on the Optimizely feed and requires CMS UI Core 13.0.0 or higher. Confirm the published version before you take a dependency, but it is available.

In flight, with code you can read

Everything below has an open or merged upgrade branch you can inspect.

Geta.Optimizely.Sitemaps. PR #143 was merged into the upgrade/cms13 branch on 29 May after a full review cycle, covering the PropertySEOSitemaps accessibility fix, the ISynchronizedObjectInstanceCache swap, and the no-op Graph proxy. The branch is effectively complete; what remains is cutting a published NuGet release. Treat it as code-complete with the release pending, and watch the repository’s releases page.

Geta.NotFoundHandler.Optimizely. PR #187 is open and in review, targeting .NET 10, CMS 13 and Commerce 15, and assigned to the v7.0 milestone. The diff is large: scheduled-job attributes migrated, content-link discovery moved to the application model, System.Text.Json adopted, and the Shell layout updated. The maintainer is engaged and the review is moving.

Geta.Optimizely.Tags. Issue #32 is being worked. The maintainer recently put it on the 3.0 milestone and describes it as close. It is less far along than the two above, but no longer a dead end.

DbLocalizationProvider. A cms13 branch now tracks the v9 work, which is where the GetString binary break has to be solved. The maintainer has flagged that a few planned v9 features may slip to a 9.1 follow-up so the CMS 13 build can ship sooner. Track the branch and the related issue, and keep a fallback for editor-managed localisation in case the release lands after your cut-off.

Addon.Episerver.EnvironmentSynchronizer (Epinova). A week ago this was an unanswered issue. PR #30 is now open, scoped to .NET 10, CMS 13 and the application framework, and it has already started removing the old NuGet packaging.

EPiServer.Labs.LanguageManager. Best read I have is that a CMS 13 release is targeted this quarter, likely around June — treat that as my expectation, not a commitment. This is the package that the IContentRouteResolver change took down, so a maintained build is the proper fix. Before you wait for it, check whether CMS 13’s native translation workflow (Translate Content plus machine auto-translation) already covers your use of Language Manager. For many solutions it now does, in which case you can drop the add-on rather than upgrade it.

Retired in favour of a native feature: MaxMind geolocation

This is the one package with a definite “no”, and the cleanest case to resolve.

EPiServer.Personalization.MaxMindGeolocation doesn’t look like it’s getting migrated, and I haven’t seen any signal that one is planned. The bundled GeoLite data source has been heading toward deprecation for years, so this is a reasonable point to drop it. The replacement ships in CMS 13: client geolocation via AddCmsClientGeolocation(), which reads location from your CDN or proxy headers rather than a local database.

On DXP (CloudPlatform): nothing to do. CloudPlatform already calls AddCmsClientGeolocation() for you, with the Cloudflare header configuration wired up.

On PaaS or self-hosted: register it yourself in ConfigureServices. The minimal form:

services.AddCmsClientGeolocation();

For a custom CDN or proxy, point it at the right headers:

services.AddCmsClientGeolocation(options =>
{
    options.IPAddressHeader = "X-Forwarded-For";
    options.LocationHeader = "CF-IPCountry"; // or your own header
});

If you sit behind a chain of proxies, tell CMS how many to trust, in appsettings.json:

"CMS": {
  "ClientGeolocation": {
    "IPAddressHeader": "X-Forwarded-For",
    "IPAddressHeaderProxyCount": 3
  }
}

One caveat: in CMS 13, geolocation-driven personalisation through Audiences works on in-process (traditional) sites, not on headless ones. Confirm your hosting model before you design around it. If you need a custom criterion, you can still call the official MaxMind.GeoIP2 SDK directly; what’s gone is the old bundled GeoLite provider, not MaxMind itself.

Roadmap context: Content Delivery API and Commerce 15

If your solution leans on the Content Delivery API (CDA) or Commerce, factor in the wider timeline. A CDA build for CMS 13 is on the roadmap, but without an ETA: it is gated behind stabilising the CMS and Commerce core, and behind the Commerce 15 release, which is still in preview and expected in H2 2026. The package name is expected to stay the same, though some endpoint or feature scope may be trimmed. Optimizely’s stated direction is to push catalog content delivery toward Content Graph rather than the REST API, which is worth weighing if you are designing new integrations now rather than retrofitting existing ones.

The practical takeaway is to decouple your Commerce and CDA timing from your CMS timing. A CDA dependency with no ETA should not hold back a CMS 13 upgrade that is otherwise ready.

How to sequence your own upgrade

  1. Inventory every add-on against this list before you commit to a date. The blockers, not the CMS core, are the real critical path. With six of them now in flight, that path is considerably shorter than it was a week ago.
  2. Validate at runtime, not at build time. The three breaking changes above all surface as TypeLoadExceptions that a successful build will not catch.
  3. Prefer native CMS 13 capabilities where they now exist: the translation workflow instead of Language Manager, AddCmsClientGeolocation instead of the GeoLite provider.
  4. Read the upgrade PRs for the packages you depend on. They are the most current documentation of what the migration requires, and they tell you whether a release is days or weeks out.
  5. Decouple Commerce and CDA timing from the CMS core upgrade.

The state of the CMS 13 add-on ecosystem has changed substantially in about a week: most of the packages that were blocking now have upgrade code in review or merged. If you parked your upgrade planning because the dependencies weren’t ready, it is worth re-checking the specific packages you rely on. For several of them, the answer has changed from “not yet” to a branch you can read.

Sources (verify before you act)

// tech stack
Optimizely CMS 13.NET 10Content GraphNuGetASP.NET Core