How to use CKEditor 5 in Laravel

In the past, I have written an article on using CKEditor 4 in Laravel. One of the readers asked about integrating CKEditor 5 in Laravel. As a programmer, I don’t have an interest in finding a comparison between CKEditor 4 and 5. My job is to integrate them into the project. If you are interested in comparison please check out this page.

Why CKEditor?

While building a website, most of the time we require to add rich content to the database. This includes media, headings, text formatting, links, styles, etc. To add such content you require a rich text editor(WYSIWYG editor). These types of editors allow you to add HTML as content which is otherwise not possible through the textarea tag.

In CKEditor, you will get the options for adding rich content. This content you can grab on the server-side and store into the database. On the front-end, you just need to fetch from the database and print it.

When it comes to adding images, this feature is not available by default in CKEditor 5. It requires you to add some code which I’ll show you in the later part of the tutorial. First, let’s see how to install CKEditor 5 in Laravel.


Installation of CKEditor 5 is super easy. You can either download their package or include it through CDN.

The users can get the package on the CKEditor 5 build download page. You can choose a different build. However, I recommend using the CKEditor 5 Classic build.


At the time of writing this article, it’s showing the latest version as 30.0.0. You may find a different version on your end.

To store this package inside Laravel, create a ckeditor directory inside the public folder. Under this ckeditor directory, paste all files from the downloaded package. In my case, the ckeditor directory has the following files.


If you go for CDN, you don’t need to download the package. Instead, you can use the CDN link into your Laravel project.

How to Use CKEditor 5 in Laravel

Once you have included package files under public/ckeditor directory, include ckeditor.js in your blade component.

<script src="{{ asset('ckeditor/ckeditor.js') }}"></script>

For CDN, you will include it as follows.

<script src=""></script>

After this, create a simple form that has two fields – CKEditor and Submit button.

<form action="{{ url('ROUTE_HERE') }}" method="post">
    <textarea class="form-control" id="editor" name="editor"></textarea>
    {{ csrf_field() }}
    <input type="submit" name="submit" value="Submit" />

Here, to convert textarea into the WYSIWYG editor write the below JavaScript.

    .create( document.querySelector( '#editor' ) )
    .catch( error => {
        console.error( error );
    } );

Now reload the page and you should now see your CKEditor instead of a textarea.


Add the rich content into the editor and submit it. On the server side, you’ll get the content using the below statement.

$content = $request->input('editor');

To populate your editor with content, all you need to add value to textarea.

<textarea class="form-control" id="editor" name="editor">Your content here</textarea>

Easy enough! Let’s check now how to allow uploading images in the CKEditor 5.

Upload Image in CKEditor 5

CKEditor 5 does not allow us to upload images directly. To upload files, it requires you to build a custom image upload adapter.

Basically, you need to write both JavaScript and server-side code in order to upload images. While uploading images, you should create a thumbnail of different sizes. This is because high-resolution images will not fit nicely inside the editor. Having multiple thumbnails makes the image responsive in the editor.

In order to achieve this, we are going to perform the below steps.

  • Implement Custom Image Uploader.
  • Resize images using Intervention image library.
  • Store the images inside the public directory.

Add Custom Image Uploader

To give an image upload feature in CKEditor 5, you need to replace the JavaScript code written above with the following one.

class MyUploadAdapter {
    constructor( loader ) {
        this.loader = loader;

    upload() {
        return this.loader.file
            .then( file => new Promise( ( resolve, reject ) => {
                this._initListeners( resolve, reject, file );
                this._sendRequest( file );
            } ) );

    abort() {
        if ( this.xhr ) {

    _initRequest() {
        const xhr = this.xhr = new XMLHttpRequest(); 'POST', "{{route('upload', ['_token' => csrf_token() ])}}", true );
        xhr.responseType = 'json';

    _initListeners( resolve, reject, file ) {
        const xhr = this.xhr;
        const loader = this.loader;
        const genericErrorText = `Couldn't upload file: ${ }.`;

        xhr.addEventListener( 'error', () => reject( genericErrorText ) );
        xhr.addEventListener( 'abort', () => reject() );
        xhr.addEventListener( 'load', () => {
            const response = xhr.response;

            if ( !response || response.error ) {
                return reject( response && response.error ? response.error.message : genericErrorText );

            resolve( response );
        } );

        if ( xhr.upload ) {
            xhr.upload.addEventListener( 'progress', evt => {
                if ( evt.lengthComputable ) {
                    loader.uploadTotal =;
                    loader.uploaded = evt.loaded;
            } );

    _sendRequest( file ) {
        const data = new FormData();

        data.append( 'upload', file );

        this.xhr.send( data );

function MyCustomUploadAdapterPlugin( editor ) {
    editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
        return new MyUploadAdapter( loader );

    .create( document.querySelector( '#editor' ), {
        extraPlugins: [ MyCustomUploadAdapterPlugin ],
    } )
    .catch( error => {
        console.error( error );
    } );

If you notice, I have used the route ‘upload’ in the above code. When we pick up the image, CKEditor sends the image to this route for server-side processing. Let’s define this route as follows.

Route::post('editor/image_upload', 'EditorController@upload')->name('upload');

Next, create the EditorController using the command:

php artisan make:controller EditorController

As we need to upload an image on the server, create a symbolic link to the storage folder.

php artisan storage:link

Upload Image On A Server Side

As I mentioned, we should create a thumbnail of the image. For instance, I am going to create a thumbnail with a width of 500px. That means we will have 2 images on the server and these images will be sent in response to CKEditor.

For creating a thumbnail of the image install the Intervention Image library through the command:

composer require intervention/image

Upon installation, open config/app.php and add a service provider to the $providers array.


Similarly, add the facade to the $aliases array.

'Image' => Intervention\Image\Facades\Image::class

Next, add this facade ‘Image’ to your EditorController as follows.

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Image;
class EditorController extends Controller

Finally, in the upload method of the controller, we write a code that will upload a file on the server, create a thumbnail and send both images in the response.

public function upload(Request $request)
    if($request->hasFile('upload')) {
        //get filename with extension
        $filenamewithextension = $request->file('upload')->getClientOriginalName();
        //get filename without extension
        $filename = pathinfo($filenamewithextension, PATHINFO_FILENAME);
        //get file extension
        $extension = $request->file('upload')->getClientOriginalExtension();
        //filename to store
        $filenametostore = $filename.'_'.time().'.'.$extension;
        //Upload File
        $request->file('upload')->storeAs('public/uploads', $filenametostore);
        $request->file('upload')->storeAs('public/uploads/thumbnail', $filenametostore);

        //Resize image here
        $thumbnailpath = public_path('storage/uploads/thumbnail/'.$filenametostore);
        $img = Image::make($thumbnailpath)->resize(500, 150, function($constraint) {

        echo json_encode([
            'default' => asset('storage/uploads/'.$filenametostore),
            '500' => asset('storage/uploads/thumbnail/'.$filenametostore)

I am storing the original image inside the storage/uploads and thumbnail image into the storage/uploads/thumbnail directory.

CKEditor would then receive the response and add both images through src and srcset attributes in the img tag.

It’s all about adding CKEditor 5 in Laravel. Give it a try and let me know your thoughts in the comment section below.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

Leave a Reply

Your email address will not be published.