---
title: Migrate to Optimizely Forms 6.0.0
slug: cms-13/docs/forms-6.0.0-migration
docTags: 
createdAt: 2026-07-30T20:22:06.928Z
---

Optimizely Forms 6.0.0 runs on Optimizely Content Management System (CMS) 13 and .NET 10. Complete the CMS 13 migration before upgrading Forms.

## Prerequisites

- .NET 10 or later
- CMS 13 migration completed

## Migration steps

Complete each step to upgrade your project from Forms 5.x to 6.0.0.

1. **Update NuGet package references** – Update the Forms NuGet package version references to 6.0.0 in your `.csproj`:

```xml
<PackageReference Include="EPiServer.Forms.Core" Version="6.0.0" />
<PackageReference Include="EPiServer.Forms.UI" Version="6.0.0" />
<!-- Or using umbrella package: -->
<PackageReference Include="EPiServer.Forms" Version="6.0.0" />
<!-- If using Azure Key Vault crypto: -->
<PackageReference Include="EPiServer.Forms.Crypto.AzureKeyVault" Version="3.0.0" />
```

Remove the following package reference (the package no longer exists):

```xml
<PackageReference Include="EPiServer.AddOns.Helpers" />
```

2. **Register Forms services in DI** – Forms 6.0.0 drops the old `[ServiceConfiguration]` auto-registration pattern (where possible). You must now call the Forms extension methods explicitly in your `Program.cs` or `Startup.cs`.
   - **Before (5.10.7)** – Services were auto-registered with `InitializationModule.ConfigureContainer()` and `[ServiceConfiguration]` attributes. No explicit registration required.
   - **After (6.0.0)** – Add the following code in `Program.cs` or `Startup.cs` to register all Forms, FormsUI, and FormsCore services:

```csharp
builder.Services.AddForms(); 
```

:::Paragraph{indent="2"}
If you need only partial registration:
:::

```csharp
builder.Services.AddFormsCore();   // Core only
builder.Services.AddFormsUI();     // UI + Core
builder.Services.AddForms();       // Full stack (recommended)
```

:::Paragraph{indent="2"}
For Azure Key Vault encryption:
:::

```csharp
builder.Services.AddFormsCryptoAzureKeyVault();
```

3. **Migrate configuration from** `Forms.config` **to** `appsettings.json`. Forms 6.0.0 no longer supports the `Forms.config` XML file. Move all settings to `appsettings.json`.
   - `Forms.config` **before (5.10.7)**

```xml
<EPiServerForms
  WorkInNonJSMode="false"
  InjectFormOwnJQuery="false"
  InjectFormOwnStylesheet="true"
  SendMessageInHTMLFormat="true"
  DefaultUploadExtensionBlackList=".exe,.bat,.cmd"
  RenderingFormUsingDivElement="false"
  DisableFormCookies="false"
  HidePartialSubmissions="false"
/>
```

:::Paragraph{listStyleType="disc" indent="2"}
`appsettings.json` **after (6.0.0)**
:::

```json
{
  "Forms": {
    "WorkInNonJSMode": false,
    "InjectFormOwnJQuery": false,
    "InjectFormOwnStylesheet": true,
    "SendMessageInHTMLFormat": true,
    "DefaultUploadExtensionBlackList": ".exe,.bat,.cmd",
    "RenderingFormUsingDivElement": false,
    "DisableFormCookies": false,
    "HidePartialSubmissions": false
  }
}
```

:::Paragraph{indent="2"}
Also move your storage provider configuration from `Forms.config` to `appsettings.json`:
:::

```json
{
  "Forms": {
    "Providers": {
      "DefaultProvider": "ddsProvider",
      "ddsProvider": {
        "type": "EPiServer.Forms.Core.Data.Internal.DdsEncryptedPermanentStorage, EPiServer.Forms.Core"
      }
    }
  }
}
```

4. **Remove references to deleted interfaces and classes** – Forms 6.0.0 removed `IEPiServerFormsCoreConfig`, `IEPiServerFormsUIConfig`, and the `EPiServer.Forms.Configuration` namespace.
   - `IEPiServerFormsCoreConfig` and `IEPiServerFormsUIConfig`
     **Before**

```csharp
public class MyService
{
    private readonly IEPiServerFormsCoreConfig _config;
    public MyService(IEPiServerFormsCoreConfig config) { _config = config; }
    public bool IsDisabled => _config.DisableFormCookies;
}
```

:::Paragraph{indent="2"}
**After**
:::

```csharp
using Microsoft.Extensions.Options;
using EPiServer.Forms.Core.Options;

public class MyService
{
    private readonly FormsConfigOptions _config;
    public MyService(IOptions<FormsConfigOptions> config) { _config = config.Value; }
    public bool IsDisabled => _config.DisableFormCookies;
}
```

:::Paragraph{listStyleType="disc" indent="2"}
`EPiServer.Forms.Configuration` **namespace** – Remove all `using EPiServer.Forms.Configuration;` directives and replace with `appsettings.json`-based configuration.
:::

5. **Update custom post-submission actors** – Remove `ActiveExternalFieldMappingTable` usage and overrides of removed methods from `SendEmailAfterSubmissionActor`. Remove overrides of `GetFriendlySummaryText` and `GetPredefinedPlaceHolders` from `SendEmailAfterSubmissionActor` subclasses. The logic now lives in `DefaultPlaceHolderProvider`.
   **Before**

```csharp
public class MyActor : PostSubmissionActorBase
{
    public override object Run(object input)
    {
        var table = ActiveExternalFieldMappingTable; // REMOVED
        // ...
    }
}
```

**After**

```csharp
public class MyActor : PostSubmissionActorBase
{
    private readonly IExternalFieldMappingService _externalFieldMappingService;

    public MyActor(IExternalFieldMappingService externalFieldMappingService)
    {
        _externalFieldMappingService = externalFieldMappingService;
    }

    public override object Run(object input)
    {
        var table = _externalFieldMappingService.GetActiveFieldMappingTable(FormIdentity);
        // ...
    }
}
```

6. **Update** `IExcludeValidatorTypes` **implementations** – Replace `IExcludeValidatorTypes` with `IAvailableValidatorTypesAttribute`.
   **Before**

```csharp
public class MyElement : ValidatableElementBlockBase, IExcludeValidatorTypes
{
    public Type[] ValidatorTypesToBeExcluded => new[] { typeof(RegularExpressionValidator) };
}
```

**After**

```csharp
[AvailableValidatorTypes(Include = new[] { typeof(RequiredValidator) })]
public class MyElement : ValidatableElementBlockBase
{
    // Use AvailableValidatorTypesAttribute instead (AvailableValidatorTypesAttribute inherits from IAvailableValidatorTypesAttribute)
}
```

7. **Update subclasses of** `InitializationModule` – If you have a partial class or subclass of `EPiServer.Forms.EditView.InitializationModule`:
   - Remove any `ConfigureContainer(ServiceConfigurationContext)` override and move its logic to `services.AddForms()` registration.
   - Remove overrides of `Instance_DeletedContent` and `Instance_PublishedContent`. If you need to handle these events, subscribe directly to `IContentEvents`.
8. **Replace removed** `FormBusinessService` **method calls** – Replace the following removed `FormBusinessService` method calls:
   - `ReCalculateStepIndexIfNeeded(FormIdentity, FormContainerBlock, Submission, int, Guid, bool)` – Replace with `ReCalculateStepIndexIfNeeded(FormContainerBlock, Submission, int, bool)`.
   - `IsMatchDependCondition(IElementDependant, IDictionary<string, object>)` – Replace with `IsConditionMatched(IElementDependant, object submittedFieldValue)`.
   - `ReCalculateStepIndex(FormContainerBlock, int, IDictionary<string, object>, bool)` – Replace with `ReCalculateStepIndexIfNeeded(FormContainerBlock, Submission, int, bool)`.
   - `BuildStepsAndElements(FormContainerBlock, bool)` – Use overload requiring `currentForm` parameter.
   - `ToHtmlStringWithFriendlyUrls(XhtmlString)` – Replace with `XhtmlRenderService.ToHtmlStringWithFriendlyUrls(...)`.
   - `ReplaceFragmentWithFriendlyUrls(IStringFragment)` – Replace with `XhtmlRenderService.ReplaceFragmentWithFriendlyUrls(...)`.
9. **Replace removed extension methods** – Replace the following removed extension methods:
   - `FormsExtensions.HasAnyExternalSystem()` – Replace with `ExternalSystemService.HasExternalSystem()`.
   - `contentRef.GetContent(string language)` (two-parameter) – Replace with `contentRef.GetContent(bool shouldFallback, string language)`.
   - `str.SplitBySeparator(string separator)` – Replace with `str.SplitBySeparators(string[] separators)`.
   - `FormsExtensionsUI.RenderWebFormViewToString(...)` – Use standard ASP.NET Core view rendering.
   - `IExternalFieldMappingService.GetAllExternalSystems()` – Replace with `ExternalSystemService.GetAllExternalSystems()`.
   - `ExternalSystemEditorDescriptors.GetSelectedFieldsName(FormIdentity, string)` – Use its protected method `GetSelectedFieldsName(FormContainerBlock, ElementBlockBase, string)`.
10. **Remove form data usage** – `IForm.Data` and `Form.Data` usage.
    **Before**

```csharp
var data = form.Data; // PropertyBag
```

**After**

```csharp
var data = submission.Data; // IDictionary<string, object>
```

11. **Update subclasses** – Update subclasses of `PropertyGenericList<T>`.
    **Before**

```csharp
public override object SaveData(PropertyDataCollection properties)
{
    return base.SaveData(properties);
}
```

**After**

```csharp
public override object SaveData()
{
    return base.SaveData();
}
```

12. **Review** `PropertyGenericList<T>` **model types for** `System.Text.Json` **compatibility** – `PropertyGenericList<T>` uses `System.Text.Json` with camelCase naming. Update model types that use `Newtonsoft.Json`-specific attributes.
    **Before**

```csharp
using Newtonsoft.Json;

public class MyModel
{
    [JsonIgnore]
    public string InternalProp { get; set; }

    [JsonProperty("my_key")]
    public string MyKey { get; set; }
}
```

**After**

```csharp
using System.Text.Json.Serialization;

public class MyModel
{
    [JsonIgnore]
    public string InternalProp { get; set; }

    [JsonPropertyName("my_key")]
    public string MyKey { get; set; }
}
```

:::hint{type="warning"}
**Important**
Existing data serialized with `Newtonsoft.Json` (camelCase or otherwise) round-trips correctly when the property names match. Verify each model type used in `PropertyGenericList<T>`.
:::

13. **Update scheduled job references** – If you reference scheduled job types directly, switch to `IScheduledJobRepository`.
    **Before**

```csharp
var job = new UpdateUploadFolderACLJob(); // REMOVED (now internal)
```

**After**

```csharp
var job = _scheduledJobRepository.Get(Guid.Parse("EB289EAA-5485-4886-A59C-5C5E0B950199"));
```

14. **Update** `SpecializedProperties` **if subclassed** – If you subclass any `SpecializedProperties` type, verify database property definition names are consistent. CMS 13 does not allow spaces in property definition names, so the new display names are without spaces. The following display names changed:
    - `Dependency conditions collection` – Changed to `DependencyConditionsCollection`.
    - `Message Template` – Changed to `MessageTemplate`.
    - `Property Field Mapping Collection` – Changed to `PropertyFieldMappingCollection`.
    - `Property Connected Data Source Collection` – Changed to `PropertyConnectedDataSourceCollection`.
    - `Validator with message collection` – Changed to `ValidatorWithMessageCollection`.
    - `Web Hook` – Changed to `WebHook`.

:::hint{type="info"}
**Note**
CMS syncs these definitions automatically on startup. Update custom code that queries property definitions by name to use the updated names.
:::

15. **Update** `FreeGeolocationProvider` **configuration** – If you configured `FreeGeolocationProvider` in `Forms.config` or `web.config` provider XML, update it to `appsettings.json`.
    **Before**

```xml
<providers>
  <add name="FreeGeoIP" type="..." geoApiUrl="https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY" />
</providers>
```

**After**

```csharp
services.AddFreeGeolocationProvider(options =>
{
    options.GeoApiUrl = "https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY";
});

// OR

services.AddFreeGeolocationProvider("https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY");
```

:::hint{type="info"}
**Note**
The `EPiServer.CloudPlatform.Cms` package registers a default `IGeolocationProvider`. Register the Forms `FreeGeoLocationProvider` after `AddCmsCloudPlatformSupport(...)` so it takes precedence.

```csharp
 services.AddCmsCloudPlatformSupport(_configuration); 
 services.AddFreeGeolocationProvider("https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY");
```
:::

16. **Update custom actor constructors** – Remove parameterless constructors that use `ServiceLocator` from custom actors. Let the DI container provide dependencies instead.
    **Before**

```csharp
public class MyActor : PostSubmissionActorBase
{
    public MyActor() : this(ServiceLocator.Current.GetInstance<MyService>()) { }
    public MyActor(MyService service) { ... }
}
```

**After**

```csharp
public class MyActor : PostSubmissionActorBase
{
    public MyActor(MyService service) { ... }
}
```

## Troubleshoot common migration errors

Resolve errors encountered during or after the Forms 6.0.0 upgrade by matching symptoms to fixes.

### Missing `services.AddForms()` call

- **Symptom** – `InvalidOperationException: Unable to resolve service for type 'EPiServer.Forms.Core.Internal.DataSubmissionService'`.
- **Fix** – Add `services.AddForms()` in `Program.cs`.

### `Forms.config` settings silently ignored

- **Symptom** – Behaviors configured in `Forms.config` (such as `WorkInNonJSMode` and upload denylist) no longer apply after upgrade.
- **Fix** – Migrate all settings to `appsettings.json` under the `Forms` key.

### `PropertyGenericList` deserialization failures

- **Symptom** – Editors show empty lists or errors for properties that previously had data.
- **Fix** – Check that your model types are `System.Text.Json`-compatible. Verify camelCase property naming matches stored JSON. Consider a one-time data migration if necessary.

### Scheduled jobs not found

- **Symptom** – Accessing `UpdateUploadFolderACLJob` and others causes a compile or runtime error.
- **Fix** – Use `IScheduledJobRepository.Get(Guid)` with the job GUIDs instead.

### Azure Key Vault crypto engine not registered

- **Symptom** – Decrypted CSV export not available, or `IFormCryptoEngine` not resolved.
- **Fix** – Call `services.AddFormsCryptoAzureKeyVault()` in addition to `services.AddForms()`.

### Email not sent after upgrade

- **Symptom** – Emails fail to send and logs show errors mentioning `ISmtpClient`.
- **Fix** – Verify that `FormsSmtpClient` (MailKit) is registered with `services.AddFormsUI()`. Verify SMTP settings in `appsettings.json`.

## Migration checklist

Verify each item before finalizing the upgrade.

- Update `EPiServer.Forms` NuGet package to 6.0.0.
- Remove `EPiServer.AddOns.Helpers` package reference.
- Add `services.AddForms()` in `Program.cs`.
- Migrate `Forms.config` settings to `appsettings.json`.
- Remove all references to `IEPiServerFormsCoreConfig`, `IEPiServerFormsUIConfig`, `EPiServer.Forms.Configuration.*`.
- Update or remove custom `InitializationModule` subclass code.
- Update custom actors by removing `ActiveExternalFieldMappingTable` and updating constructors.
- Remove `IExcludeValidatorTypes` implementations and use `IAvailableValidatorTypesAttribute`.
- Replace removed `FormBusinessService` methods with their non-obsolete equivalents.
- Replace removed `FormsExtensions` and `FormsExtensionsUI` extension methods.
- Update `PropertyGenericList` model types for `System.Text.Json` compatibility.
- Update `SaveData()` overrides to remove parameter.
- Verify scheduled job GUIDs are accessible in `IScheduledJobRepository` if needed.
- Verify property definitions in CMS database after upgrade.
- Test existing form submission data for deserialization correctness.
- Configure geo provider URL in `appsettings.json` if used.
- Call `services.AddFormsCryptoAzureKeyVault()` if using AzureKeyVault crypto.
