Spent almost 2 days figuring out a proper way to use CKEditor5 image uploader. I’ve tried ckFinder, SimpleUploader, etc. but none of them worked maybe because none of the documentations made any sense to me 😂. Luckily, I found a Stack Overflow conversation and somehow got what I needed working with just some minor tweaks.
My Goal
Every time a user copy and paste, drag and drop, or upload an image into my text editor, It will trigger an API that saves the image into an S3 bucket and returns the image S3 URL, then embeds that image back to the text editor.
Installation:
Install CKEditor5 to react here.
Implementation:
Editor Code
import React, { useState } from "react";
import { CKEditor } from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
function Editor() {
const [editorData, setEditorData] = useState();
return (
<div>
<CKEditor
editor={ClassicEditor}
data={editorData}
onReady={(editor) => {
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
setEditorData(data);
console.log({ event, editor, data });
}}
onBlur={(event, editor) => {
console.log("Blur.", editor);
console.log(editorData)
}}
onFocus={(event, editor) => {
console.log("Focus.", editor);
}}
/>
</div>
);
}
export default Editor;
output
Till now you can face the issue (as in below image) while uploading the image in editor.
due to this issue your image will not attach into editor. to resolve this issue you have to add the image toolbar plugin in editor config.
Here is the editor updated code:
import React, { useState } from "react";
import { CKEditor } from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
function Editor() {
const [editorData, setEditorData] = useState();
return (
<div>
<CKEditor
editor={ClassicEditor}
data={editorData}
onReady={(editor) => {
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
setEditorData(data);
console.log({ event, editor, data });
}}
onBlur={(event, editor) => {
console.log("Blur.", editor);
console.log(editorData)
}}
onFocus={(event, editor) => {
console.log("Focus.", editor);
}}
config={{
image: {
toolbar: [
'imageTextAlternative',
'toggleImageCaption',
'imageStyle:inline',
'imageStyle:block',
'imageStyle:side',
]
},
}}
/>
</div>
);
}
export default Editor;
Now you will see the image after inserting it in editor.
Uploading Image to server
Till now you must noticed it that every thing in the we are adding or writing in saved into single state variable. the problem will arise when you want to to have the Full Stack application Editor will all feature. Because getting all content of editor getting in on place cause the issue of extracting the image/video files before saving the text in database, as you know it is not recommended to store the files into database.
Here the challenge of extracting the files form single object arise. because we don't know at which line and where the files are added/attached in the editor.
to overcome this issue we will use the custom image uploader adopter which will take the file at the time of attaching to editor and send it to server and get the result/url back and automatically it will render that uploaded url into editor
here is the code for that
// CustomeUoload.js
class CustomUploadAdapter {
constructor(loader) {
// The file loader instance to use during the upload.
this.loader = loader;
}
upload() {
return this.loader.file.then(file => new Promise((resolve, reject) => {
// Customize your upload logic here.
// but for siplicity i am using the dummy url and evrytime sending back the same request
const url = "https://upload.wikimedia.org/wikipedia/commons/thumb/7/77/Google_Images_2015_logo.svg/220px-Google_Images_2015_logo.svg.png"
resolve({ default: url })
}));
}
abort() {
// Abort the upload process if needed.
}
}
export default function CustomUploadAdapterPlugin(editor) {
console.log(editor)
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new CustomUploadAdapter(loader);
};
}
Now you have to call the above CustomUploadAdapterPlugin
component into your editor and add it into config as follows.
...
config={{
...
extraPlugins: [CustomUploadAdapterPlugin],
}}
...
Actual Code
Here’s the full code, no more talking. Enjoy! but not too much 😊.
editor.js file
// editor.js
import React, { useState } from "react";
import { CKEditor } from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
import CustomUploadAdapterPlugin from "./customUpload";
function Editor() {
const [editorData, setEditorData] = useState();
return (
<div>
<CKEditor
editor={ClassicEditor}
data={editorData}
onReady={(editor) => {
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
setEditorData(data);
console.log({ event, editor, data });
}}
onBlur={(event, editor) => {
console.log("Blur.", editor);
console.log(editorData)
}}
onFocus={(event, editor) => {
console.log("Focus.", editor);
}}
config={{
image: {
toolbar: [
'imageTextAlternative',
'toggleImageCaption',
'imageStyle:inline',
'imageStyle:block',
'imageStyle:side',
]
},
extraPlugins: [CustomUploadAdapterPlugin],
}}
/>
</div>
);
}
export default Editor;
costomupload.js file
// costomupload.js
class CustomUploadAdapter {
constructor(loader) {
// The file loader instance to use during the upload.
this.loader = loader;
}
upload() {
return this.loader.file.then(file => new Promise((resolve, reject) => {
// Customize your upload logic here.
// but for siplicity i am using the dummy url and evrytime sending back the same request
const url = "https://upload.wikimedia.org/wikipedia/commons/thumb/7/77/Google_Images_2015_logo.svg/220px-Google_Images_2015_logo.svg.png"
resolve({ default: url })
}));
}
abort() {
// Abort the upload process if needed.
}
}
export default function CustomUploadAdapterPlugin(editor) {
console.log(editor)
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new CustomUploadAdapter(loader);
};
}
Now open the terminal type below command if you’re using create-react-app
npm start
And here is our final result
Conclusion
Thanks for reading my article ❤, if you have any problem just comment below and I will help you.
If the post was useful to you, leave me a clap 👏 and follow me, it helps me a lot. Thank you ❤️
Checkout my Blogger, Twitter, Linkedin and GitHub for more amazing content!
Top comments (0)