TypeError: moduleClass is not a constructor in React-Quill with Quill Mention Module
Author: Kuldeeptak-neelnetworksCreated Sep 5, 2024Updated Oct 22, 2025
I'm encountering an error while integrating the quill-mention module with react-quill in a Next.js application. The error message I see is:
Unhandled Runtime Error
TypeError: moduleClass is not a constructor
Call Stack
SnowTheme.addModule
(node_modules/react-quill/node_modules/quill/dist/quill.js (6130:0))
SnowTheme.addModule
(node_modules/react-quill/node_modules/quill/dist/quill.js (6774:0))
eval
(node_modules/react-quill/node_modules/quill/dist/quill.js (6122:0))
Array.forEach
SnowTheme.init
Here is how I’m trying to use the quill-mention module:
"use client";
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
import dynamic from "next/dynamic";
import Quill from "quill";
import Mention from "quill-mention";
import "react-quill/dist/quill.snow.css";
import { Button } from "@/components/ui/button";
import { Loader2 } from "lucide-react";
import TooltipCommon from "@/components/common/TooltipCommon";
import { useCopywriterStore } from "@/Store/CopywriterStore";
import { useUserStore } from "@/Store/UserStore";
import { useEditorStore } from "@/Store/EditorStore";
// Dynamically import ReactQuill to prevent SSR issues
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
interface QuillEditorProps {
customerId: string | string[];
updateId: string | string[];
indicatorText: string | string[];
handleEdit: string | string[];
orderId: string | string[];
productFlowId: string | string[];
leadId: string | string[];
technicalId: string | string[];
copywriterId: string | string[];
amendmentId: string | string[];
websiteContentId: string | string[];
setIsOpenReplyModel: Dispatch>;
setOpenQuill: Dispatch>;
}
const QuillEditor: React.FC = ({
customerId,
setIsOpenReplyModel,
setOpenQuill,
updateId,
productFlowId,
orderId,
technicalId,
leadId,
indicatorText,
amendmentId,
copywriterId,
websiteContentId,
handleEdit,
handlesave,
editContent,
}: any) => {
const [value, setValue] = useState("");
const [images, setImages] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [fileURLs, setFileURLs] = useState<{ [key: string]: string }>({});
const { fetchCopywriterData }: any = useCopywriterStore();
const { fetchUsersData, userData } = useUserStore();
const {
fetchEditorData,
fetchLeadsEditorData,
fetchOrderEditorData,
fetchTechnicalUpdateData,
fetchAmendmentUpdateData,
fetchProductFlowUpdateData,
fetchWebsiteContentUpdateData,
orderEditorData,
fetchCopywriterUpdateData,
editorData,
addReplyData,
addUpdateData,
loading,
}: any = useEditorStore();
const handleClear = () => {
setValue("");
};
const handleChanges = (value: string, editorData: any) => {
setValue(() =>
editorData.getText().trim() === "" && value === "" ? "" : value
);
};
const handleAddData = async (e: React.FormEvent) => {
e.preventDefault();
if ((value !== "" && value !== "
`;
setValue((prevValue) => prevValue + img + `
`); }; reader.readAsDataURL(file); } else { const url = URL.createObjectURL(file); setFileURLs((prev) => ({ ...prev, [file.name]: url })); const link = `${file.name}`; setValue((prevValue) => prevValue + link + `
`); } }); } }; const imageHandler = () => { const inputImage = document.createElement("input"); inputImage.setAttribute("type", "file"); inputImage.setAttribute( "accept", "image/*, video/*, .pdf, .xlsx, .doc, .docx" ); inputImage.setAttribute("multiple", "true"); inputImage.click(); inputImage.onchange = (e) => { handleFileUpload(inputImage.files); }; }; useEffect(() => { return () => { Object.values(fileURLs).forEach((url) => URL.revokeObjectURL(url)); }; }, [fileURLs]); // Commented out because it causes an error // useEffect(() => { // if (Quill) { // Quill.register("modules/imageResize", ImageResize); // Quill.register("modules/mention", Mention); // } // }, [Quill]); const toolbarOptions = [ ["bold", "italic", "underline", "strike"], [{ size: ["small", false, "large", "huge"] }], [{ list: "ordered" }, { list: "bullet" }, { list: "check" }], [{ script: "sub" }, { script: "super" }], [{ indent: "-1" }, { indent: "+1" }], [{ direction: "rtl" }], [{ color: [] }, { background: [] }], [{ font: [] }], [{ align: [] }], ]; const options = { debug: "info", modules: { toolbar: toolbarOptions, imageResize: { parchment: Quill.import("parchment"), modules: ["Resize", "DisplaySize"], }, mention: { allowedChars: /^[A-Za-z\s]*$/, mentionDenotationChars: ["@"], source: async ( searchTerm: string, renderItem: (data: any[]) => void ) => { try { await fetchUsersData(); // Ensure data is fetched first const filteredUsers = userData .filter((user: any) => user.name.toLowerCase().includes(searchTerm.toLowerCase()) ) .map((user: any) => ({ value: user.name })); renderItem(filteredUsers); } catch (error) { console.error("Error fetching mention users:", error); renderItem([]); } }, }, }, placeholder: "Compose an epic...", theme: "snow", }; return ( <>
</>
);
};
export default QuillEditor;
Question:
I've integrated the quill-mention module into my react-quill component but am running into an issue. The error TypeError: moduleClass is not a constructor appears, and the call stack indicates a problem with SnowTheme.addModule in the Quill.js file.
I've tried to register the module as follows:
`useEffect(() => {
if (Quill) {
Quill.register("modules/imageResize", ImageResize);
Quill.register("modules/mention", Mention);
}
}, [Quill]);`
However, this causes the mentioned error. I've also commented out the registration as it seems to be the cause of the issue.
What I've Tried:
Ensuring that the quill-mention module is correctly imported and used.
Checking the version compatibility of react-quill, quill, and quill-mention.
Referencing the Quill documentation for proper module registration.
What I Need Help With:
Why am I encountering this TypeError?
How can I properly integrate the quill-mention module with react-quill?
Are there any additional steps or configurations required to avoid this error?
Any guidance or suggestions would be greatly appreciated!
This is the Error:-

`); }; reader.readAsDataURL(file); } else { const url = URL.createObjectURL(file); setFileURLs((prev) => ({ ...prev, [file.name]: url })); const link = `${file.name}`; setValue((prevValue) => prevValue + link + `
`); } }); } }; const imageHandler = () => { const inputImage = document.createElement("input"); inputImage.setAttribute("type", "file"); inputImage.setAttribute( "accept", "image/*, video/*, .pdf, .xlsx, .doc, .docx" ); inputImage.setAttribute("multiple", "true"); inputImage.click(); inputImage.onchange = (e) => { handleFileUpload(inputImage.files); }; }; useEffect(() => { return () => { Object.values(fileURLs).forEach((url) => URL.revokeObjectURL(url)); }; }, [fileURLs]); // Commented out because it causes an error // useEffect(() => { // if (Quill) { // Quill.register("modules/imageResize", ImageResize); // Quill.register("modules/mention", Mention); // } // }, [Quill]); const toolbarOptions = [ ["bold", "italic", "underline", "strike"], [{ size: ["small", false, "large", "huge"] }], [{ list: "ordered" }, { list: "bullet" }, { list: "check" }], [{ script: "sub" }, { script: "super" }], [{ indent: "-1" }, { indent: "+1" }], [{ direction: "rtl" }], [{ color: [] }, { background: [] }], [{ font: [] }], [{ align: [] }], ]; const options = { debug: "info", modules: { toolbar: toolbarOptions, imageResize: { parchment: Quill.import("parchment"), modules: ["Resize", "DisplaySize"], }, mention: { allowedChars: /^[A-Za-z\s]*$/, mentionDenotationChars: ["@"], source: async ( searchTerm: string, renderItem: (data: any[]) => void ) => { try { await fetchUsersData(); // Ensure data is fetched first const filteredUsers = userData .filter((user: any) => user.name.toLowerCase().includes(searchTerm.toLowerCase()) ) .map((user: any) => ({ value: user.name })); renderItem(filteredUsers); } catch (error) { console.error("Error fetching mention users:", error); renderItem([]); } }, }, }, placeholder: "Compose an epic...", theme: "snow", }; return ( <>
{
handleChanges(value, editor);
}}
/>
Source: zenoamaro/react-quill