How to Integrate Cloudinary with TinyMCE for Image Uploads
Tags: react, tutorial, learning
Published: 2024-06-15 15:33:18

If you're looking to enable image uploads in TinyMCE and store those images in Cloudinary, you're in the right place. This guide will walk you through the steps to integrate Cloudinary with TinyMCE, allowing users to upload images directly from the editor and have them stored in Cloudinary.

###Prerequisites

1. A Cloudinary account. If you don’t have one, [sign up here](https://cloudinary.com/users/register/free).
2. A TinyMCE API key. You can get one from the [TinyMCE website](https://www.tiny.cloud/get-tiny/self-hosted/).

#### Steps

1. **Set Up Cloudinary**

   - **Log in to Cloudinary**: Go to the [Cloudinary website](https://cloudinary.com/) and log in to your account.
   - **Navigate to Settings**: Once logged in, click on the "Settings" tab.
   - **Create an Upload Preset**:
     - Go to the "Upload" tab.
     - Scroll down to the "Upload Presets" section and click "Add upload preset".
     - Configure the preset according to your needs (e.g., allowed formats, transformations, folder, etc.).
     - Save the preset and note its name.

2. **Set Up TinyMCE**

   - Install TinyMCE in your project (if you haven't already):
     ```bash
     npm install @tinymce/tinymce-react
     ```

   - Create a component for the TinyMCE editor and integrate the Cloudinary upload functionality:
     ```javascript
     import React, { useRef } from 'react';
     import { Editor } from '@tinymce/tinymce-react';

     interface TinyMiceEditorProps {
         editorRef: React.MutableRefObject<any>;
     }

    export default function TinyMiceEditor({ editorRef }:TinyMiceEditorProps) {
       const filePickerCallback = (callback, value, meta) => {
         if (meta.filetype === 'image') {
           const input = document.createElement('input');
           input.setAttribute('type', 'file');
           input.setAttribute('accept', 'image/*');

           input.onchange = async function () {
             const file = this.files[0];

             if (file) {
               const formData = new FormData();
               formData.append('file', file);
               formData.append('upload_preset', 'your_upload_preset'); // replace with your upload preset

               try {
                 const response = await fetch(
                   `https://api.cloudinary.com/v1_1/your_cloud_name/image/upload`, // replace with your cloud name
                   {
                     method: 'POST',
                     body: formData,
                   }
                 );

                 if (!response.ok) {
                   throw new Error('Network response was not ok');
                 }

                 const data = await response.json();
                 const imageUrl = data.secure_url;

                 // Insert the uploaded image URL into TinyMCE
                 callback(imageUrl, { title: file.name });
               } catch (error) {
                 console.error('Error uploading image to Cloudinary:', error);
               }
             }
           };

           input.click();
         }
       };

       return (
         <Editor
           apiKey={process.env.NEXT_PUBLIC_TINYMCE_API_KEY}
           onInit={(_evt, editor) => (editorRef.current = editor)}
           initialValue=""
           init={{
             height: 500,
             menubar: false,
             plugins: [
               "advlist",
               "autolink",
               "lists",
               "link",
               "image",
               "charmap",
               "preview",
               "anchor",
               "searchreplace",
               "visualblocks",
               "code",
               "fullscreen",
               "insertdatetime",
               "media",
               "table",
               "code",
               "help",
               "wordcount",
             ],
             toolbar:
               "undo redo | blocks | image " +
               "bold italic forecolor | alignleft aligncenter " +
               "alignright alignjustify | bullist numlist outdent indent | " +
               "removeformat | help",
             content_style:
               "body { font-family:Helvetica,Arial,sans-serif; font-size:14px }",
             file_picker_callback: filePickerCallback,
           }}
         />
       );
     };

     export default MyEditor;
     ```

### Explanation

   1. **File Input**: The `filePickerCallback` creates a file input element to allow users to select an image.
   3. **Fetch POST Request**: The image is uploaded to Cloudinary using the `fetch` API. Replace `your_cloud_name` and `your_upload_preset` with your Cloudinary details.
   4. **Image URL**: On successful upload, Cloudinary returns the image URL inserted into TinyMCE using the `callback`.

### Important Notes

- **Security**: Using unsigned uploads without an upload preset might expose your Cloudinary account to misuse. Using signed uploads or configuring strict settings on your Cloudinary account is recommended.
- **Configuration**: Upload presets allow you to enforce consistent transformations, naming conventions, and security settings, which can be very beneficial for maintaining order and control over your media assets.

And that's it! You've successfully integrated Cloudinary with TinyMCE to allow image uploads directly from the editor. This setup provides a seamless experience for users to add images to their content, with the images being securely stored and managed in Cloudinary.