title: How to connect a custom image selector to CKeditor5 insert image in react js
body: <p>I am trying to intergrate CKeditor into a custom document editor, using ckeditor5-react module it was easy to intergrate the state into the data however the default behaviour for inserting an image is not desirable in my particular use case. I have a image library built into my application so I'd like the CKeditor button to open my image library component so the user can select the image from the library, then insert that image where the cursor is placed.
  After trying to acheive this with the regular, out-of-the-box ClassicEditor build I realised it was not going to be possible so I created a custom plugin that should achieve what I want:</p>
  
  <pre><code>import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
  
  import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
  import Autoformat from '@ckeditor/ckeditor5-autoformat/src/autoformat';
  import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
  import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
  import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
  import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
  import Heading from '@ckeditor/ckeditor5-heading/src/heading';
  import Image from '@ckeditor/ckeditor5-image/src/image';
  import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle';
  import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
  import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload';
  import Link from '@ckeditor/ckeditor5-link/src/link';
  import List from '@ckeditor/ckeditor5-list/src/list';
  import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed';
  import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice';
  import Table from '@ckeditor/ckeditor5-table/src/table';
  import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
  
  import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
  import mix from '@ckeditor/ckeditor5-utils/src/mix';
  
  import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  
  import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  
  import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
  
  export default class ClassicEditor extends ClassicEditorBase {}
  
  class InsertImage extends Plugin {
      init() {
      const editor = this.editor;
  
      editor.ui.componentFactory.add( 'insertImage', locale =&gt; {
        const view = new ButtonView( locale );
  
        view.set( {
          label: 'Insert image',
                  icon: imageIcon,
          tooltip: true,
        } );
  
        // set observables on editor
        editor.set( { 
          insertImageRequested: false,
          imageFileRequested: null
        } );
  
  
          // Callback executed once the image button is clicked.
        view.on( 'execute', () =&gt; {
          // set observable to indicate a request to insert image has been made...
          editor.set( { insertImageRequested: true } );
        });
  
        // Now this waits for the user to have selected a file URL which should show up 
        // in the imageFileRequested observable
        editor.on( 'change:imageFileRequested', (evt, newShizz, oldShizz) =&gt; {
          // // Which then injects the image into the body...
          const imageUrl = editor.imageFileRequested;
          editor.model.change( writer =&gt; {
            const imageElement = writer.createElement( 'image', {
              src: imageUrl
            } );
            // Insert the image in the current selection location.
            editor.model.insertContent( imageElement, editor.model.document.selection );
          });
  
          // and unsets our observables back to their original state
          editor.set( { 
            insertImageRequested: false,
            imageFileRequested: null
          } );
        } )
  
              return view;
          } );
      }
  }
  mix( InsertImage, ObservableMixin );
  
  // Plugins to include in the build.
  ClassicEditor.builtinPlugins = [
      Essentials,
      Autoformat,
      Bold,
      Italic,
      BlockQuote,
      EasyImage,
      Heading,
      Image,
      ImageStyle,
      ImageToolbar,
      ImageUpload,
      Link,
      List,
      MediaEmbed,
      Paragraph,
      PasteFromOffice,
      Table,
      TableToolbar,
      InsertImage
  ];
  
  // Editor configuration.
  ClassicEditor.defaultConfig = {
      toolbar: {
          items: [
              'heading',
              '|',
              'bold',
              'italic',
              'link',
              'bulletedList',
              'numberedList',
              'blockQuote',
              'insertTable',
              'mediaEmbed',
              'undo',
              'redo',
              '|',
              'InsertImage'
          ]
      },
      image: {
          toolbar: [
              'imageStyle:full',
              'imageStyle:side',
              '|',
              'imageTextAlternative'
          ]
      },
      table: {
          contentToolbar: [
              'tableColumn',
              'tableRow',
              'mergeTableCells'
          ]
      },
      // This value must be kept in sync with the language defined in webpack.config.js.
      language: 'en'
  };
  </code></pre>
  
  <p>From the above code you can see that it uses the Observable events feature of CKeditor5 to set 2 observables: insertImageRequested (bool) or imageFileRequested (null or string).
  Now in my react component I recieve the "insertImageRequested" change event and update my state so that the library should open and my component does that ok (however the cursor does move to the beginning of the document, which I dont want). The problem is I just cant seem to get the editor to allow me to set the second Observable "imageFileRequested" from outside of the editor with the selected images URL, so I cant send the fileURL string back to the custom editor to set the Observable </p>
  
  <pre><code>import React from 'react';
  import PropTypes from 'prop-types';
  
  ...
  
  import CKEditor from '@ckeditor/ckeditor5-react';
  //import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
  import CustomEditor from './customCKeditor/ckeditor.js';
  
  import styles from './articleEditor.scss'; 
  
  class ArticleEditor extends React.Component {
    constructor(props) {
      super(props);
  
      this.state = {
        insertImageRequested: false,
        imageFileRequested: null,
        eventLogData: null
      }
  
      ...
  
      this.imageClick = this.imageClick.bind(this);
    }
  
    ...
  
    imageClick(){
  // Temp Data to be passed to custom Ckeditor
      let tempURL = "https://via.placeholder.com/350x150";
      this.setState({
        insertImageRequested: false,
        imageFileRequested: tempURL
      })
  
    // HOW DO I PASS 'tempURL' variable to editor so that I can set it as the value for the imageFileRequested observable (so CKeditor on change event is triggered as shown above)?
  
    }
  
    render() {
      return(
          &lt;div className={styles.container}&gt;
            &lt;CKEditor
              editor={ CustomEditor }
              data={this.state.articleBody}
              name={'articleBody'}
  
              config={{
                  toolbar: [ 'InsertImage', '|', 'heading', '|', 'bold', 'italic', '|', 'link', 'bulletedList', 'numberedList', 'blockQuote', '|', "mediaEmbed", '|', 'undo', 'redo', '|', "insertTable", "tableColumn", "tableRow", "mergeTableCells", '|' ],
                  heading: {
                      options: [
                          { model: 'paragraph', title: 'Paragraph' },
                          { model: 'heading1', view: 'h1', title: 'Heading 1'},
                          { model: 'heading2', view: 'h2', title: 'Heading 2'},
                          { model: 'heading3', view: 'h3', title: 'Heading 3'},
                          { model: 'heading4', view: 'h4', title: 'Heading 4'},
                          { model: 'heading5', view: 'h5', title: 'Heading 5'},
                          { model: 'heading6', view: 'h6', title: 'Heading 6'}
                      ]
                  }
              }}
  
              onInit={ editor =&gt; {
                // A request for a new image has been made
                editor.on( 'change:insertImageRequested', (evt, newShizz, oldShizz) =&gt; {
                  //reflect that in the react app state
                  this.setState({
                    insertImageRequested: true
                  })
                } )
              } }
  
              onChange={ ( event, editor ) =&gt; {
                  console.log(event);
                  console.log(editor);
                  //console.log(editor.insertImageRequested);
                  const data = editor.getData();
                  console.log( { event, editor, data } );
                  this.updateArticleBodyState(data);
                  this.eventLogger(editor);
  
                  // THIS DOES NOT WORK
                  // //if(editor.imageFileRequested != null){
                  //     console.log("imagefilerequested is defined!")
                  //     editor.set( { imageFileRequested: this.state.imageFileRequested } );
                  // //}
  
              } }
              onBlur={ editor =&gt; {
                  //console.log( 'Blur.', editor );
              } }
              onFocus={ editor =&gt; {
                  //console.log( 'Focus.', editor );
              } }
            /&gt;
          &lt;/div&gt;
      );
    }
  }
  
  
  ...
  </code></pre>
  
  <p>After 2 days of wrestling with this, with the solution going from relatively straightforward to needlessly complex it occurred to me that the way I am doing this is most likely not right and my entire approach has been wrong, so I suppose my question is two-fold. First, is the above approach to what I am doing the correct way?
  If it is, can you help me set the tempUrl variable as the observable in ckeditor.
  Secondly if this is completely the wrong approach can you please help me find the correct way to go about implementing this. I am happy to provide more information if its needed however this post has become very long so I'll end with, thanks in advance for any help you can provide.</p>