#4437·quill

Incorrect List Insertion in Quill Editor

Author: ST-NANDHAGOPANCreated Oct 3, 2024Updated Jul 28, 2026
**Description:** When using the bullet tool in the Quill editor, both a bullet and a number are inserted simultaneously, even when the numbered list tool is not included in the editor configuration. ![Screenshot from 2024-10-03 12-07-28](https://github.com/user-attachments/assets/e9345d3d-3285-47af-aacd-b5b99e163dc5) **Environment:** Quill Version: 2.0.2 React Version: 18.2.0 **Steps to Reproduce:** 1. Set up a Quill editor instance with the following toolbar options: ``` const toolbarOptions = [ [{ font: [] }, { header: [1, 2, 3, 4, 5, 6, false] }], ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }, { align: [] }], ["link", "image", "video"], ["blockquote", "code-block"], ["clean"], [{ color: [] }, { background: [] }], ["hr", "emoji"], ]; ``` 2. Click on the bullet tool in the toolbar. **Expected Behavior:** The editor should insert a bullet point within an unordered list `(
    ) `without any numbering. **Actual Behavior**: The editor inserts a bullet point within an ordered list `(
      )` , resulting in HTML content like this: `
      1. dfdsrffddfdf
      ` **Question: How can I fix this issue so that the bullet tool inserts items in an unordered list format? Any guidance or suggestions would be greatly appreciated!** This is My Full code. ``` import React, { useEffect, useRef, useState } from "react"; import Quill from "quill"; import "quill/dist/quill.snow.css"; import { Mention, MentionBlot } from "quill-mention"; import "quill-mention/dist/quill.mention.css"; import EmojiPicker from "emoji-picker-react"; const BlockEmbed = Quill.import("blots/block/embed") as any; class DividerBlot extends BlockEmbed { static blotName = "divider"; static tagName = "hr"; } class EmojiReactionBlot extends BlockEmbed { static blotName = "emoji-reaction"; static tagName = "span"; static className = "emoji-reaction"; static create(emoji) { const node = super.create(); node.setAttribute("data-emoji", emoji); node.innerHTML = emoji; return node; } } Quill.register({ "blots/mention": MentionBlot, "modules/mention": Mention, "blots/divider": DividerBlot, "blots/emoji-reaction": EmojiReactionBlot, }); const atValues = [ { id: 1, value: "Fredrik Sundqvist" }, { id: 2, value: "Patrik Sjölin" }, ]; const toolbarOptions = [ [{ font: [] }, { header: [1, 2, 3, 4, 5, 6, false] }], ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }, { align: [] }], ["link", "image", "video"], ["blockquote", "code-block"], ["clean"], [{ color: [] }, { background: [] }], ["hr", "emoji"], ]; const emojiPickerStyles: React.CSSProperties = { position: "absolute", zIndex: 1000, top: "60px", right: "20px", width: "300px", height: "400px", }; const QuillEditor = ({ value, handleTextEditorChange }) => { const editorRef = useRef(null); const fileInputRef = useRef(null); const emojiPickerRef = useRef(null); const quillInstance = useRef(null); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [cursorPosition, setCursorPosition] = useState(null); useEffect(() => { // Initialize Quill editor only once if (!quillInstance.current) { const icons = Quill.import("ui/icons") as Record; icons.hr = ''; icons.emoji = ''; const Font = Quill.import("formats/font") as any; Font.whitelist = ["sans-serif", "serif", "monospace"]; Quill.register(Font, true); const quill = new Quill(editorRef.current as HTMLElement, { theme: "snow", modules: { mention: { allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/, mentionDenotationChars: ["@"], source: function (searchTerm, renderList, mentionChar) { const values = mentionChar === "@" ? atValues : []; const matches = values.filter((item) => item.value.toLowerCase().includes(searchTerm.toLowerCase()) ); renderList(matches.length ? matches : values, searchTerm); }, }, toolbar: { container: toolbarOptions, handlers: { image: () => fileInputRef.current?.click(), emoji: () => { const cursor = quill.getSelection(); if (cursor) { setCursorPosition(cursor.index); setShowEmojiPicker((prev) => !prev); } else { setCursorPosition(null); } }, hr: () => { const range = quill.getSelection(); if (range) { quill.insertEmbed(range.index, "divider", true); } }, }, }, }, }); quillInstance.current = quill; // Add text-change event listener to handle editor content changes quill.on("text-change", () => { const editorContent = quill.root.innerHTML; handleTextEditorChange(editorContent); }); quillInstance.current = quill; } // Only update the editor content if the value changes if (quillInstance.current && value !== quillInstance.current.root.innerHTML) { quillInstance.current.root.innerHTML = value; } }, [value, handleTextEditorChange]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( emojiPickerRef.current && !emojiPickerRef.current.contains(event.target as Node) && showEmojiPicker ) { setShowEmojiPicker(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [showEmojiPicker]); const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file && quillInstance.current) { const reader = new FileReader(); reader.onload = () => { const base64Image = reader.result as string; const range = quillInstance?.current?.getSelection(); quillInstance.current?.insertEmbed(range?.index || 0, "image", base64Image); }; reader.readAsDataURL(file); } }; const logEditorContent = () => { if (quillInstance.current) { console.log("HTML Content:", quillInstance.current.root.innerHTML); } }; const onEmojiClick = (emojiData: { emoji: string }) => { if (cursorPosition !== null && quillInstance.current) { const emoji = emojiData.emoji; quillInstance.current.focus(); quillInstance.current.insertEmbed(cursorPosition, "emoji-reaction", emoji); setCursorPosition(cursorPosition + emoji.length); } setShowEmojiPicker(false); }; return ( {showEmojiPicker && ( )} ); }; export default QuillEditor; ```