CKEditor Image upload with Firebase and React
Published: 2020-09-09 15:49:25

CKEditor is an awesome opensource project that offers tons of tools similar to Microsoft word. I have been using this editor on the website that I recently created (https://www.codingnotesonline.com/). However, for the image upload option in CKEditor, it requires a server to store the images. The examples mentioned in the documents are specifically based on PHP for the backend side and you can find more examples of similar implementation in Nodejs. The problem was that I was using firebase for all the backend worked and didn't find a proper example to implement it with firebase.

**How to Implement CKEditor Image upload with Firebase in React ?**

I have used firebase upload functions and added in custom adapter function of CKEditor.
Firebase(upload function):(https://firebase.google.com/docs/storage/web/upload-files)
CKEditor(custom adapter):(https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/upload-adapter.html)

In the below example, **MyUploadAdapter** is the custom adapter in which we use a firebase upload function that returns a URL, which is a link to an image stored in the firebase server. This link is then used by the CKEditor to display it on the page.

Firebase also offers several upload stages that you can use to show the progress of image upload to the user.

Below is a complete working example that you can implement in your project. 

```js
import React from "react";
import CKEditor from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
//import firbase from your directory


class MyUploadAdapter {
  constructor(loader) {
    this.loader = loader;
  }
  // Starts the upload process.
  upload() {
    return this.loader.file.then(
      file =>
        new Promise((resolve, reject) => {
          let storage = firebase.storage().ref();
          let uploadTask = storage
            .child(file.name)
            .put(file, metadata);
          uploadTask.on(
            firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
            function(snapshot) {
              // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
              var progress =
                (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
              console.log("Upload is " + progress + "% done");
              switch (snapshot.state) {
                case firebase.storage.TaskState.PAUSED: // or 'paused'
                  console.log("Upload is paused");
                  break;
                case firebase.storage.TaskState.RUNNING: // or 'running'
                  console.log("Upload is running");
                  break;
              }
            },
            function(error) {
              // A full list of error codes is available at
              // https://firebase.google.com/docs/storage/web/handle-errors
              // eslint-disable-next-line default-case
              switch (error.code) {
                case "storage/unauthorized":
                  reject(" User doesn't have permission to access the object");
                  break;

                case "storage/canceled":
                  reject("User canceled the upload");
                  break;

                case "storage/unknown":
                  reject(
                    "Unknown error occurred, inspect error.serverResponse"
                  );
                  break;
              }
            },
            function() {
              // Upload completed successfully, now we can get the download URL
              uploadTask.snapshot.ref
                .getDownloadURL()
                .then(function(downloadURL) {
                  // console.log("File available at", downloadURL);
                  resolve({
                    default: downloadURL
                  });
                });
            }
          );
        })
    );
  }
}

class App extends Component {
    render() {
        return (
            <div className="App">
                <h2>Using CKEditor 5 build in React</h2>
                <CKEditor
                    editor={ ClassicEditor }
                    data="<p>Hello from CKEditor 5!</p>"
                    onInit={editor => {
                      editor.plugins.get("FileRepository").createUploadAdapter = loader => {
                        return new MyUploadAdapter(loader);
                      };
                    }}
                    onChange={ ( event, editor ) => {
                        const data = editor.getData();
                        console.log( { event, editor, data } );
                    } }
                    onBlur={ ( event, editor ) => {
                        console.log( 'Blur.', editor );
                    } }
                    onFocus={ ( event, editor ) => {
                        console.log( 'Focus.', editor );
                    } }
                />
            </div>
        );
    }
}
```
This is a boilerplate code that you can use and make changes as per your needs. I hope this would help you and saved a lot of your time finding the solution to it.