---
canonical: "https://www.jsolly.com/blog/migrating-to-ckeditor-5/"
title: "Demystifying the CKEditor 4 to 5 Migration Process: Lessons Learned"
description: "Learn how to migrate from CKEditor 4 to CKEditor 5 and strengthen your Content Security Policy (CSP) by removing unsafe-inline scripts | A Guide"
author: "John Solly"
published: "2022-05-27T18:45:25.000Z"
updated: "2022-05-27T18:45:25.000Z"
---

<a id="demystifying-the-ckeditor-4-to-5-migration-process-lessons-learned"></a>

# Demystifying the CKEditor 4 to 5 Migration Process: Lessons Learned

![A picture of a toad chilling in the sand.](https://d1d7p8ufhgz4ld.cloudfront.net/media/post_metaimgs/toad.jpg)

Migrating ckeditor4 to CKEditor is not straightforward.

> _When compared to its predecessor, CKEditor 5 should be considered **a totally new editor**. Every single aspect of it was redesigned — from installation, to integration, to features, to its data model, and finally to its API. Therefore, moving applications using a previous CKEditor version to version 5 cannot be simply called an "upgrade". It is something bigger, so the "migration" term fits better._
> 
> [_Ckeditor5 Migration Doc_](https://ckeditor.com/docs/ckeditor5/latest/installation/getting-started/migration-from-ckeditor-4.html)

I moved to CKEditor 5 to overcome roadblocks hit while [implementing a CSP (content security policy](https://www.jsolly.com/blog/how-to-implement-content-security-policy-django/)). Ckeditor4 uses unsafe-inline styling and scripts, which weaken a CSP. I noticed active work on a new Python module, [django-ckeditor-5](https://pypi.org/project/django-ckeditor-5/), so I created a branch and started experimenting.

<a id="existing-plugins"></a>

## Existing Plugins

<table style="border:1px solid black"><tbody><tr><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">Ckeditor 4</p></td><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">CKEditor 5</p></td></tr><tr><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">mathjax</p></td><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">MathType</p></td></tr><tr><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">wordcount</p></td><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">word-count</p></td></tr><tr><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">Code Snippets</p></td><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">Code Blocks</p></td></tr><tr><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">scayt</p></td><td style="border:1px solid black;padding:5px"><p style="margin-left:0px">Proofreader</p></td></tr></tbody></table>

The most significant lift was migrating from [Code Snippets](https://ckeditor.com/docs/ckeditor4/latest/features/codesnippet.html) to [Code Blocks](https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html). CKEditor 5 has a much simpler integration for syntax highlighting. Instead of a  [Prism.js](https://prismjs.com/) plugin, I simply included Prism.js as a script in my templates. It works by choosing your languages from [Prism's download page](https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript) and then configuring Code Blocks to match that same set of languages. Prism doesn't just offer support for language syntax. It also includes support for git commands and Apache config files.

<pre><code class="language-json">"codeBlock": {
    "languages": [
        {"language": "python", "label": "Python"},
        {"language": "css", "label": "CSS"},
        {"language": "yaml", "label": "YAML"},
        {"language": "json", "label": "JSON"},
        {"language": "git", "label": "Git"},
        {"language": "sql", "label": "SQL"},
        {"language": "html", "label": "HTML"},
        {"language": "bash", "label": "BASH"},
        {"language": "javascript", "label": "JavaScript"},
        {"language": "apacheconf", "label": "ApacheConf"},
    ]
}</code></pre>

CKEditor 5's downside is that I can't see syntax highlighting while in edit mode.

> _Although live code block highlighting **is impossible when editing** in CKEditor 5 (_[_learn more_](https://github.com/ckeditor/ckeditor5/issues/436#issuecomment-548399675)_), the content can be highlighted when displayed in the frontend (e.g. in blog posts, messages, etc.)._
> 
> [_Integrating with code highlighters_](https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html#integration-with-code-highlighters)
> 
> _\-_ [_CKEditor Syntax Highlighting_](https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html#integration-with-code-highlighters)

<a id="image-uploads"></a>

## Image Uploads

At first, I couldn't upload images. I got a 403 Forbidden response with a message about a missing CSRF token. Someone had [logged an issue](https://github.com/hvlads/django-ckeditor-5/issues/18), and the [maintainer replied](https://github.com/hvlads/django-ckeditor-5/issues/18#issuecomment-817617907) he created the module to work on the admin page, not the front-end. After reading [this stack overflow thread](https://stackoverflow.com/questions/17507800/how-do-i-modify-the-file-upload-handlers-in-a-class-based-view-with-csrf-middlew) and reviewing how [FileUpload works](https://docs.djangoproject.com/en/4.0/topics/http/file-uploads/), I solved it myself. I published my workaround in a [second issue](https://app.prowritingaid.com/second%20issue). Hope it gets resolved in the official module soon!

The second issue was that image uploads were not going into /media/uploads like the [django-ckeditor](https://django-ckeditor.readthedocs.io/en/latest/) implementation. In CKEditor 4, you set the upload path using a variable in settings.py that specifies where you want the uploaded image to go. I logged [an enhancement](https://github.com/hvlads/django-ckeditor-5/issues/64) since I didn't know how to do it in django-ckeditor-5. The [maintainer commented](https://app.prowritingaid.com/he%20maintainer%20commented%20back) that a custom storage class could control where CKEditor 5 uploads images. He also added a snippet on the [home page of the repo](https://github.com/hvlads/django-ckeditor-5) so others can benefit.

<a id="inline-scripts-and-styles"></a>

## Inline Scripts and Styles

When I removed unsafe-inline scripts from my CSP, there weren't any errors in the console as in CKEditor 5.! Unfortunately, it failed with inline styles. I logged [another issue,](https://github.com/hvlads/django-ckeditor-5/issues/65) and the [maintainer replied](https://github.com/hvlads/django-ckeditor-5/issues/65#issuecomment-1140424863) that it's not an issue with CKEditor 5 but with one or more of its plugins. It's up to me to either remove those plugins or [add hashes to my CSP](https://content-security-policy.com/hash/) to accommodate them.

<a id="form-field-widgets"></a>

## Form Field Widgets

Another migration issue I ran into was that [form widgets](https://docs.djangoproject.com/en/4.0/ref/forms/widgets/) no longer work with text content fields. I [logged an issue](https://github.com/hvlads/django-ckeditor-5/issues/62), and the [maintainer replied](https://github.com/hvlads/django-ckeditor-5/issues/62#issuecomment-1140284738) with a working example of how to fix it! Instead of `forms.Textarea,` I should use `CKEditor5Wigdet.`He even added the example to the [home page](https://github.com/hvlads/django-ckeditor-5). Hopefully, it helps others!

<a id="conclusion"></a>

## Conclusion

Migrating from CKEditor 4 to CKEditor 5 was no easy feat, having to make several configuration changes to migrate plugins. I had issues with image uploads, form widgets, and inline styles. The good news is that CKEditor 5 allowed me to strengthen my CSP by removing unsafe-inline scripts. Blogthedata.com now has an [A+ in Mozilla Observatory](https://observatory.mozilla.org/analyze/blogthedata.com)! Another benefit is that through my struggles, I helped improve the module. Over the next couple of weeks, I will use CKEditor 5 on production and fix any defects [in the PR](https://github.com/jsolly/blogthedata/pull/82). 

May 27, 2022 in [Web Dev](https://www.jsolly.com/blog/category/web-dev/)

Updated May 27, 2022
