CKEditor 5 Image Upload Plugin in SharePoint Framework (SPFx)

Steps to build a custom plugin to upload images in CKEditor5 (Classic Editor) Rich Text Editor.

In this blog, we will learn to build a custom image upload plugin to upload images to CKEditor5 (Classic Editor) using SharePoint Framework (SPFx).

What is Classic Editor in CKEditor5?
 
Classic editor is what most users traditionally associate with a rich text editor — a toolbar with an editing area placed in a specific position on the page, usually as a part of a form that you use to submit some content to the server.
 
Note: Ckeditor5 Classic Editor is not yet supported in IE11 browser check here.
 
Below code tested in Chrome, Mozilla and Edge.
 
Code Usage:
 
Install "ckeditor5-classic" from your node package manager console as shown below.
  1. PS C:\XXXX\SPFxSolutions\SpFxRichTextEditor>npm install --save ckeditor5-classic    
Import "Classic Editor" to your tsx file. 
  1. import ClassicEditor from 'ckeditor5-classic';  
Create "MyUploadAdapter.ts" file as shown below.
  1. /* this is my custom Image file adapter */  
  2. export default class MyUploadAdapter {  
  3. loader:any;  
  4. reader:any;  
  5. constructor(loader) {  
  6. // The file loader instance to use during the upload.  
  7. this.loader = loader;  
  8. }  
  9. //Starts the upload process.  
  10. upload() {  
  11. return this.loader.file  
  12. .then(file => new Promise((resolve, reject) => {  
  13. this._initListeners(resolve, reject, file);  
  14. }));  
  15. }  
  16. _initListeners(resolve, reject, file) {  
  17. console.log(file.name);  
  18. const reader = this.reader = new FileReader();  
  19. reader.addEventListener('load', () => {  
  20. resolve({ default: reader.result });  
  21. });  
  22. reader.addEventListener('error', err => {  
  23. reject(err);  
  24. });  
  25. reader.addEventListener('abort', () => {  
  26. reject();  
  27. });  
  28. this.loader.file.then(file => {  
  29. reader.readAsDataURL(file);  
  30. reader.result  
  31. });  
  32. }  
  33. abort() {  
  34. this.reader.abort();  
  35. }  
  36. }  
Import "MyUploadAdapter" to your tsx file.
  1. import MyUploadAdapter from  './MyUploadAdapter';    
Import "Jquery" plugin to your tsx file. 
  1. import * as $ from 'jquery';   
Initiate default toolbar configuration settings for Classic Editor.
  1. ClassicEditor.defaultConfig = {      
  2.   toolbar: {      
  3.     items: [      
  4.       'heading',      
  5.       '|',      
  6.       'bold',      
  7.       'italic',      
  8.       'fontSize',      
  9.       'fontFamily',      
  10.       'fontColor',      
  11.       'fontBackgroundColor',      
  12.       'link',      
  13.       'bulletedList',      
  14.       'numberedList',      
  15.       'imageUpload',      
  16.       'insertTable',      
  17.       'blockQuote',      
  18.       'undo',      
  19.       'redo'      
  20.     ]      
  21.   },      
  22.   image: {      
  23.     toolbar: [      
  24.       'imageStyle:full',      
  25.       'imageStyle:side',      
  26.       '|',      
  27.       'imageTextAlternative'      
  28.     ]      
  29.   },      
  30.   fontFamily: {      
  31.     options: [      
  32.       'Arial',      
  33.       'Helvetica, sans-serif',      
  34.       'Courier New, Courier, monospace',      
  35.       'Georgia, serif',      
  36.       'Lucida Sans Unicode, Lucida Grande, sans-serif',      
  37.       'Tahoma, Geneva, sans-serif',      
  38.       'Times New Roman, Times, serif',      
  39.       'Trebuchet MS, Helvetica, sans-serif',      
  40.       'Verdana, Geneva, sans-serif'      
  41.     ]      
  42.   },      
  43.   language: 'en'      
  44. };      
Declare the state variable to store ckeditor event
  1. export interface ICKeditorState {      
  2.   CKEditorEvent:any;      
  3. }  
Initiatlize the state variable in your constructor
  1. constructor(props:  ISpFxRichTextEditorProps) {      
  2.     super(props);      
  3.       this.state = {      
  4.         CKEditorEvent:''             
  5.     };          
  6.   }      
  7. }   
Insert text area to render the CKeditor5 on load.
  1. public render(){      
  2.     return (      
  3.       <div>      
  4.         <textarea id="editor1"></textarea>       
  5.       </div>      
  6.     );      
  7.   }  
Replace text area with CKEditor5 on componentDidMount.
  1. componentDidMount(){      
  2.   this.InitializeCKeditor();      
  3. }  
  1. /* Load CKeditor RTE*/    
  2.   public InitializeCKeditor(): void {    
  3.     try {    
  4.       /*Replace textarea with classic editor*/    
  5.       ClassicEditor    
  6.         .create(document.querySelector("#editor1"), {    
  7.         extraPlugins: [this.MyUploadAdapterPlugin]            
  8.         }).then(editor => {    
  9.           console.log("CKEditor5 initiated");   
  10.           /*store the ckeditor event in state variable */         
  11.           this.setState({CKEditorEvent:editor});  
  12.         }).catch(error => {    
  13.           console.log("Error in Classic Editor Create " + error);    
  14.         });    
  15.     } catch (error) {    
  16.       console.log("Error in  InitializeCKeditor " + error);    
  17.     }    
  18.   }    
Define Custom image upload adapter plugin
  1. public MyUploadAdapterPlugin(editor) {    
  2.   editor.plugins.get('FileRepository').createUploadAdapter = function (loader) {    
  3.     /* Initialize MyUploadAdapter class to initiate custom upload adapter */    
  4.     return new MyUploadAdapter(loader);    
  5.   };    
  6. }   
Please feel free to share your comments.
 
Hope this helps !!!!!