> For the complete documentation index, see [llms.txt](https://docs.umbraco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.umbraco.com/umbraco-cms/get-started/upgrading-and-migrating/find-your-upgrade-path/single-block-migration.md).

# Single Block Migration for Umbraco 18

Learn how to migrate Block List property editors configured in single mode to  the new Single Block property editor.

Version 17 introduced the single block property editor. Its purpose is to replace the "single mode" option that exists in the Block List property editor. This is part of the broader effort to ensure type consistency across core property editors.

## Included migration

Umbraco ships with a migration to:

* Update all block list Data Types that have been properly configured in "single" mode.
* Update all (nested) property data that uses this Data Type.

The migration was added in Umbraco 17 but disabled. It now runs by default during the upgrade to Umbraco 18.

## Pre-running the migration

You can run the migration at any time by using your own migration plan, as shown in the example below. If you run this migration yourself, the default Umbraco migration won't update any data. It only changes data in the old format.

```csharp
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_18_0_0;

namespace SingleBlockMigrationRunner;

public class TestMigrationComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.AddNotificationAsyncHandler<UmbracoApplicationStartingNotification, RunTestMigration>();
    }
}

public class RunTestMigration : INotificationAsyncHandler<UmbracoApplicationStartingNotification>
{
    private readonly IMigrationPlanExecutor _migrationPlanExecutor;
    private readonly ICoreScopeProvider _coreScopeProvider;
    private readonly IKeyValueService _keyValueService;
    private readonly IRuntimeState _runtimeState;

    public RunTestMigration(
        ICoreScopeProvider coreScopeProvider,
        IMigrationPlanExecutor migrationPlanExecutor,
        IKeyValueService keyValueService,
        IRuntimeState runtimeState)
    {
        _migrationPlanExecutor = migrationPlanExecutor;
        _coreScopeProvider = coreScopeProvider;
        _keyValueService = keyValueService;
        _runtimeState = runtimeState;
    }

    public async Task HandleAsync(UmbracoApplicationStartingNotification notification, CancellationToken cancellationToken)
    {
        if (_runtimeState.Level < RuntimeLevel.Run)
        {
            return;
        }

        // One-off migration plan
        var migrationPlan = new MigrationPlan("Single Block Migration");

        // define the step
        migrationPlan.From(string.Empty)
            .To<MigrateSingleBlockList>("test-run-singleBlock-migration");

        // Go and upgrade our site (Will check if it needs to do the work or not)
        // Based on the current/latest step
        var upgrader = new Upgrader(migrationPlan);
        await upgrader.ExecuteAsync(
            _migrationPlanExecutor,
            _coreScopeProvider,
            _keyValueService);
    }
}

```

## Extending the migration

If your non-core property editor nests content and stores it within its own value, you must extend the migration. To do this, create and register a class that implements `ITypedSingleBlockListProcessor` and register it. See how the built-in types are registered at `Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_18_0_0.SingleBlockList.MigrateSingleBlockListComposer`. The interface needs the following properties and methods:

* `IEnumerable<string> PropertyEditorAliases`: The alias of the property editor as defined in its DataEditor attribute. Since a processor can support multiple editors if they use the same model, it takes an IEnumerable rather than a single string. These aliases are used to limit the amount of data fetched from the database.
* `Type PropertyEditorValueType`: The type of value the property editor would return when `valueEditor.ToEditor()` is called.
* `Func<object?, Func<object?, bool>, Func<BlockListValue, object>, bool> Process` The function to run when the main processor detects a value that matches your processor. The function must support the following parameters:
  * `object?`: The value passed in from the outer caller or the top-level processor.
  * `Func<object?, bool>` The function the processor calls when it detects nested content. This is passed in from the top-level processor.
  * `Func<BlockListValue, object>` The function called when the outer layer of the current value is a block list that needs to be converted to a single block. This should only be called from the core processors. It is passed around to make recursion a little easier.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.umbraco.com/umbraco-cms/get-started/upgrading-and-migrating/find-your-upgrade-path/single-block-migration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
