 [ ← blog / drupal ](/) May 09, 2023  ·  \#drupal  ·  5 min read 

# Upgrading my site from CKEditor 4 to CKEditor 5



My site runs on Drupal 9.5. I started preparing to upgrade to Drupal 10 right after 10.0.0 was released, but then I got hit with CKEditor 4 to CKEditor 5 blockers. The [Linkit](https://www.drupal.org/project/linkit), [CodeSnippet](https://www.drupal.org/project/codesnippet), and [Entity Embed](https://www.drupal.org/project/entity_embed) modules supported Drupal 10 but didn't support CKEditor 5. I *could* have updated to Drupal 10 and leveraged the CKEditor 4 contributed module, but I wanted to wait.

I will say, though. As we approach Drupal 10.1.0, more users will find a much easier upgrade path to Drupal 10 and CKEditor 5.

## Linkit module is Drupal 10  / CKEditor 5 ready!

First, the Linkit module is ready! On [5 March 2023, 6.0.0-beta4 was released](https://www.drupal.org/project/linkit/releases/6.0.0-beta4). I missed this and found the [6.1.0-rc1 release from 9 April 2023](https://www.drupal.org/project/linkit/releases/6.1.0-rc1). I had to fix my `composer.json` requirement and then run `composer update drupal/linkit`.

```diff
diff --git a/composer.json b/composer.json
index d35fb20c..051b9037 100644
--- a/composer.json
+++ b/composer.json

-        "drupal/linkit": "^5.0@beta",
+        "drupal/linkit": "^6.0@rc",
```

## CodeSnippet – now in Drupal core 10.1.0!

Well, it turns out CodeSnippet is coming to Drupal core! Drupal core has supported [CKEditor 5 code blocks since 9.3.x](https://www.drupal.org/project/drupal/issues/3263384) and a migration path from the [CKEditor CodeTag module](https://www.drupal.org/project/drupal/issues/3274278). But, the code block configuration was not exposed. Drupal 10.1.0 will now [provide this functionality out of the box](https://www.drupal.org/project/drupal/issues/3282233), mostly deprecating the CodeSnippet module! I say "mostly" because no code-highlighting libraries are provided (like Highlight.js or Prism.) There is also an issue currently RTBC'd, and I am sure to land, which provides the migration path from CodeSnippet to the CKEditor 5 code blocks plugin: <https://www.drupal.org/project/drupal/issues/3356929.>

There was just one problem. I don't want to wait for 10.1.0 to be released in June. I want to ensure everything works fine now without upgrading Drupal to 10. Luckily, both of the patches for CKEditor 5 apply to 9.5! I could add the patches to my composer.json and have the code blocks with CodeSnippet migration.

```diff
diff --git a/composer.json b/composer.json
index d35fb20c..051b9037 100644
--- a/composer.json
+++ b/composer.json

     "extra": {
+        "composer-exit-on-patch-failure": true,
+        "patchLevel": {
+            "drupal/core": "-p2"
+        },
         "patches": {
+            "drupal/core": {
+                "CKE5 configure code block": "https://git.drupalcode.org/project/drupal/-/commit/0c59ee88d4e22da61d6eea02f232a7cb519824d9.patch",
+                "CKE5 codesnippet": "https://www.drupal.org/files/issues/2023-04-27/3356929-2.patch"
+            }
         },
         "drupal-scaffold": {
             "locations": {
```

The only downside is that CodeSnippet provided Highlight.js integration out of the box. Two contributed modules are available that [Wim Leers](https://www.drupal.org/u/wim-leers) tested to provide code highlighting: [highlight.php](https://www.drupal.org/project/highlight_php) and [highlight.js Input Filter](https://www.drupal.org/project/highlightjs_input_filter). However, neither has security coverage. One module only supports PHP highlighting, while the latter supports all. The latter plugin loads all of Highlight.js and not only the required languages. Neither work exactly how I'd like.

I will write a filter plugin that checks for any `code[class^="language-"]` instances to attach the proper libraries. I'll share that once done!

## Entity Embed, a time before Media Library

My site is old. The oldest timestamp I can find is `1305360870`. That is May 14, 2011. I'm unsure if that's correct because this site has been migrated from WordPress to Drupal 7 and then to Drupal 8. Either way, I've collected some technical debt along the way. When I migrated from Drupal 7 to Drupal 8, I created a filter plugin to keep Drupal 7 media embed tokens working. I then started to use Entity Embed for placing media in my content. Then the Media Library module was released, and I stopped using Entity Embed. I never migrated anything because it just worked.

However, CKEditor 5 finally forced me to address this technical debt. [Entity Embed](https://www.drupal.org/project/entity_embed) isn't Drupal 10 ready. It almost is (see [\#3309747](https://www.drupal.org/project/embed/issues/3309747) and [\#3272732](https://www.drupal.org/project/entity_embed/issues/3272732).) Since I don't use it, I didn't want it to be a blocker. So I finally wrote a migration to update 60 blog posts away from Entity Embed.

Luckily Entity Embed and Media work nearly the same.

```xml
// entity_embed
<drupal-entity data-entity-uuid=" data-entity-type="" data-embed-button="" data-entity-embed-display="">

// media
<drupal-media data-entity-uuid=" data-entity-type="" data-view-mode="">
```

That's an easy upgrade path!

- Replace `drupal-entity` with `drupal-media`.
- Drop `data-embed-button`
- Replace `data-entity-embed-display` with `data-view-mode`

Here is the update hook I wrote to migrate the blog posts using Entity Embed in batches of 5 at a time.

```php
/**
 * @param array{'#finished': bool, ids: ?array<int, int[]>} $sandbox
*/
function mglaman_dev_update_9003(array &$sandbox): void {
  $storage = \Drupal::entityTypeManager()->getStorage('node');
  if (!isset($sandbox['ids'])) {
    $query = $storage->getQuery()->accessCheck(FALSE)->condition('body.value', '%drupal-entity%', 'LIKE');
    $ids = $query->execute();
    $sandbox['ids'] = array_chunk($ids, 5);
  }
  $batch = array_shift($sandbox['ids']);
  if (is_array($batch)) {
    /** @var \Drupal\node\NodeInterface[] $nodes */
    $nodes = $storage->loadMultiple($batch);
    foreach ($nodes as $node) {
      $body_field = $node->get('body')->first();
      assert($body_field instanceof FieldItemInterface);
      // @phpstan-ignore-next-line
      $text = $body_field->value;
      $search = [
        'drupal-entity',
        'data-embed-button="media_entity_embed"',
        'data-entity-embed-display',
        'view_mode:media.',
      ];
      $replace = [
        'drupal-media',
        '',
        'data-view-mode',
        '',
      ];
      $text = str_replace($search, $replace, $text);
      // @phpstan-ignore-next-line
      $body_field->value = $text;
      $node->save();
    }
  }

  $sandbox['#finished'] = count($sandbox['ids']) === 0;
}
```

Then I could uninstall Entity Embed and the Embed module from my site!

## Lost functionality with Editor Advanced Link

I am using the [Editor Advanced link module](https://www.drupal.org/project/editor_advanced_link) to allow setting custom `data` attributes on links within CKEditor. That approach is now broken. I don't think it'd be possible for that module to provide a fix. So I will write a custom CKEditor 5 plugin to add my custom link `data` attributes.

Edit: In the comments, Wim Leers has pointed to some documentation that may make this easier than I thought: [https://ckeditor.com/docs/ckeditor5/latest/features/link.html#adding-at…](https://ckeditor.com/docs/ckeditor5/latest/features/link.html#adding-attributes-to-links-using-the-ui-manual-decorators)

## What else am I looking forward to with CKEditor 5?

Auto formatting was just committed for Drupal 10.1.0's release! This allows typing `` ` `` and automatically getting code formatting. It's the Markdown-in-WYSIWYG formatting in Confluence, Notion, Slack, and other editors.

 [Add support for ckeditor5-autoformat

Problem/Motivation Drupal 10.0.0 will ship with CKEditor 5. The CKEditor contrib ecosystem is trending in the right direction, with 2 of the 4 modules with &gt;50K sites using them already having stable releases, a third being ready for testing and the fourth well on its way. So … it seems the time is right to start making CKEditor 5 better in Drupal core than CKEditor 4 ever was. So, we propose this: This feature is the #1 that comes to mind. It's not intrusive. It empowers power users. Try the demo.

https://www.drupal.org/project/drupal/issues/3227354

 

 ![Add support for ckeditor5-autoformat](https://www.drupal.org/sites/all/themes/bluecheese/images/og.jpg) 

 

 

 ](https://www.drupal.org/project/drupal/issues/3227354)  

  ![](/themes/custom/mglaman/images/headshot.webp)Thanks for reading

I write about Drupal, PHP, and the quiet infrastructure behind large sites — caching, config, and testing. New posts land every week or two.

 [Subscribe via RSS →](/rss.xml) [About me →](/about-me) 

 



 

  [← Older

Drupal module semantic versioning for Drupal core support

 ](/blog/drupal-module-semantic-versioning-drupal-core-support) [Newer →

Simplifying the frontend developer experience in Drupal with a click of the button

 ](/blog/simplifying-frontend-developer-experience-drupal-click-button)