← Back

Optimize React-Quill Image Storage: Upload to AWS S3 & Replace Base64 Strings

November 7, 2024

When we insert an image into the react-quill editor and then inspect the image in developer tools, the image turns out to be a base64 string. There is no harm in it, but what if we need to store the content of the react-quill editor in our database and there is a base 64 image present? To solve this issue, we need to upload the image to a cloud storage provider such as AWS S3 which will return a URL that can replace the base64 string of the image.

In this article, we will go through the steps on how to upload the image to AWS S3 when it is selected in react-quill editor.

Let’s get started

We start by installing react-quill using npm.

npm install react-quill

Import react-quill into the application

import ReactQuill from 'react-quill';

We also need to pass a ref to the editor so we can access the editor when the image is uploaded to the server.

const editorRef = useRef()

Insert the editor component in the application.

 handleChange(value)}
    placeholder="Write something..."
/>

The above component accepts some props, one of which is modules. The modules object specifies which modules to enable in the editor configuration. We have wrapped the modules object in a useMemo hook imported from React. This helps us to memoize the modules object when the component is re-rendered.

const modules = useMemo(
    () => ({
      toolbar: {
        container: [
          [{ header: [1, 2, 3, 4, 5, 6, false] }, { font: [] }],
          ["bold", "italic", "underline", "strike", "blockquote"],
          [
            { list: "ordered" },
            { list: "bullet" },
            { indent: "-1" },
            { indent: "+1" }
          ],
          ["link", "image", "video", "code-block"],
          [{ align: [] }]
        ],
        handlers: {
          image: imageHandler
        }
      }
    }),
    []
  );

The modules object contains a handler that can handle images in the editor.

const imageHandler = () => {
    const input = document.createElement("input");
    input.setAttribute("type", "file");
    input.setAttribute("accept", "image/*");
    input.click();

    input.onchange = () => {
      const file = input.files![0];

      if (/^image\//.test(file.type)) {
        saveToServer(file);
      } else {
        alert("You could only upload images.");
      }
    };
  };

In the image handler, we insert an input element to the document which can only accept image files. Once the image is selected, we make a call to saveToServer function.

const saveToServer = (file) => {
    const fd = new FormData();
    fd.append("upload", file);

    axios
      .post("/upload-to-aws", fd, {
        headers: {
          "Content-Type": "multipart/form-data"
        }
      })
      .then((res) => {
        if (res.status === 201) {
          const url = res.data.url;              
          editorRef.current?.getEditor!().insertEmbed(null, "image", url);
          }
        }
      })
      .catch((err) => {
        console.log(err.response.data.message);
      });
  };

In the above function, we insert the image file into formData and send it to the backend. When the image is successfully uploaded to AWS S3, we send the URL to the frontend. We then replaced the base64 string of the image with the URL.

Now we move on the backend where implement a route to handle the image from the frontend and upload it to S3 bucket.

To implement the route, we need to install some packages

npm i multer multer-s3 aws-sdk express

const express = require("express");
const multer = require("multer");
const AWS = require("aws-sdk");
const multerS3 = require("multer-s3");

const app = express()

AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});

var s3Storage = multerS3({
  s3: new AWS.S3(),
  bucket: process.env.AWS_S3_BUCKET,
  acl: "public-read",
  contentType: multerS3.AUTO_CONTENT_TYPE,
  key: function (request, file, cb) {
    cb(null, `${Date.now().toString()} - ${file.originalname}`);
  },
});

const upload = multer({
  storage: s3Storage,
});

app.post(
  "/upload-to-aws",
  upload.single("upload"),
  (req, res)=>{
    res.status(201).json({
      url: req.file.location,
      message: "File uploaded successfully",
    });
  }
);

Let’s dive into the code. We start by importing these packages into the application

const express = require("express");
const multer = require("multer");
const AWS = require("aws-sdk");
const multerS3 = require("multer-s3");

Next, we need to update AWS configurations by adding access key id and secret access key of the IAM user role from the AWS console.

AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});

We create a multerS3 object with the configurations which include s3 object, access control of the file, s3 bucket name, a mime type used to upload the file, and a key property which will be the filename. We then passed this object to multer which is used as a middleware at the POSTrequest. In the middleware, the image file is uploaded to the AWS S3 using the configurations provided in multerS3 object.

When the file upload is successful, we send the file location in the response to the frontend. We then replaced the base64 string of the image with the URL received from the backend.

Conclusion

Congratulations. You have finally made it. In this article, we have seen how we can replace base64 image with a URL in react-quill editor by uploading the image into AWS S3.

If you have any queries, let me know down in the comment section.

Happy coding!!!

Salman Inayat Email 𝕏 GitHub