DEV Community

Amir Najafi for CKEditor

Posted on • Updated on

How to upload different media types in ckEditor5 using custom upload Adaptor

Image description
hello my fellow developers πŸ™Œ

One of the main issues when dealing with WYSIWYG editors is uploading media (audio, video, image, etc.)
there are always limitations on how to upload certain media and of course developers have come up with professional solutions that might help you in managing your files (tools like CKBox), but what if you just want to use your personal custom editor for a change?

Step 1: Create a Nextjs project

to begin, open the terminal and run the following command:
npx create-next-app

you can also activate Typescript, ESlint and other options if you intend to use them in your project.

Step 2: Create a custom build

you can choose your own ckeditor5 toolbar and plugins by going to CKEditor Online Builder and download the package.
unzip it and then copy the folder into your project's src folder and change these lines in ckeditor5/build/ckeditor.js

acceptedType:r.map((t=>`image/${t}`)).join(","))
Enter fullscreen mode Exit fullscreen mode

to:

acceptedType:'*'
Enter fullscreen mode Exit fullscreen mode

i=Array.from(n).filter((t=>s.test(t.type)))
Enter fullscreen mode Exit fullscreen mode

to

i=Array.from(n)
Enter fullscreen mode Exit fullscreen mode

and finally run npm install file:src/ckeditor5

you should now see ckeditor5-custom-build in your package.json

you should also install ckeditor package for react
npm install @ckeditor/ckeditor5-react"

step 3: Add editor to page

make a new folder inside app directory and rename it to blog.
then create a page.tsx and CustomEditor component:

// CustomEditor.js
import { CKEditor } from '@ckeditor/ckeditor5-react'
import Editor from 'ckeditor5-custom-build'
import axios from 'axios'

// upload Adaptor
function uploadAdapter(loader) {
  return {
    upload: () => {
      return new Promise((resolve, reject) => {
        const fd = new FormData()
        loader.file.then((file) => {
          // here check the mimetype and send request
          // to relevant backend api endpoint
          axios
            .post(`https://sample.com/files/${endPoint}`, fd)
            .then((res) => {
              resolve({
                default: res.data[0].fileAddress
              })
            })
            .catch((err) => {
              reject(err)
            })
        })
      })
    },
  }
}

function uploadPlugin(editor) {
  editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
    return uploadAdapter(loader)
  }

  // when upload completes, replace tag
  const imageUploadEditing = editor.plugins.get('ImageUploadEditing')
  imageUploadEditing.on('uploadComplete', (evt, { data, imageElement }) => {
    editor.model.change((writer) => {
      const view = editor.data.processor.toView(
        data.mediaType === 'video'
          ? `<video src='${data.default}' controls="controls"></video>`
          : data.mediaType === 'audio'
          ? `<audio src='${data.default}' controls="controls"></audio>`
          : `<img src='${data.default}' />`
      )
      const model = editor.data.toModel(view)
      editor.model.insertContent(model, editor.model.document.selection)
    })

    evt.stop()
    editor.editing.view.focus()
  })
}

const editorConfiguration = {
  placeholder: 'write your content...',
  extraPlugins: [uploadPlugin],
  toolbar: {
    items: [
      'heading',
      '|',
      'uploadImage',
      '|',
      'code',
      'codeBlock',
      'showBlocks',
      'sourceEditing',
      '|',
      'undo',
      'redo',
    ],
    shouldNotGroupWhenFull: true,
  },
  simpleUpload: {
    uploadUrl: `https://sample.com/files/editor`,
    fileTypes: ['.pdf', '.doc', '.docx', '.xls', '.xlsx'],
  },
  image: {
    upload: { types: ['mp4', 'pdf', 'mpeg', 'jpg', 'png'] },
  },

  htmlSupport: {
    allow: [
      {
        name: /.*/,
        attributes: true,
        classes: true,
        styles: true,
      },
    ],
  },
}

function CustomEditor(props) {
  return (
    <CKEditor
      editor={Editor}
      rows={7}
      data={props?.content}
      config={editorConfiguration}
      onChange={(event, editor) => {
        const newData = editor.getData()
        props.handleContent(newData)
      }}
    />
  )
}

export default CustomEditor
Enter fullscreen mode Exit fullscreen mode
// blog/page.tsx

'use client';

import dynamic from 'next/dynamic.js';
import { useState } from 'react';

const CustomEditor = dynamic(
  () => {
    return import('./CustomEditor.jsx');
  },
  {ssr: false}
);

const Blog = () => {

  const [content, setContent] = useState("");

  return (
    <div>
      <CustomEditor id="content" content={content} handleContent={(newContent: any) => setContent(newContent)} />
    </div>
  );
};

export default Blog;

Enter fullscreen mode Exit fullscreen mode

you can use your custom endpoints to send data to your backend and receive the file address. basically the code tries to replace the tag in the editor using conversion

also don't forget you need to add your backend uploader for this code to work (I'm using multer and express in my own project).

Here's the result:

Uploaded Audio & Video

Top comments (2)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
seyedali72 profile image
seyedali72

tnx, it was very useful and I needed this experience.