mrkeyoor.com_
Sat 08 Aug 17:40 UTC
npmWeb Frontendupdated 08 Aug 2026

react-quill

react-quill is a React 16 through 18 wrapper around Quill 1.3.7. It renders a browser rich-text editor, exposes Quill through a component ref, accepts HTML or Delta documents, and maps React props to themes, formats, toolbar modules, selection events, and read-only mode. Its controlled mode is only a hybrid because Quill mutates its own DOM before React can reconcile it. The current README now says most React users probably do not need this wrapper and links to Quill's official direct-React example.

Verdict

Do not install react-quill for a new application. Keep it only as a legacy compatibility layer around Quill 1, then choose direct Quill 2, a maintained fork, or a different editor when you can budget the migration.

API stability3/5The v2 props and callbacks have stayed fixed, but that stability comes from inactivity and locks users to Quill 1 plus React 18 or earlier.
Docs3/5The README thoroughly describes v2 props, Deltas, toolbars, themes, and controlled-mode traps, but it also contains old CDN versions, legacy class-component examples, and aging links.
Maintenance1/5The current npm release is from August 2022, the last repository push was in February 2025, hundreds of issues and PRs are open, and the README directs new users away.
Ecosystem2/5It has a large installed base and familiar Quill modules, but it remains pinned to Quill 1 and does not declare compatibility with React 19.

Use it if

  • You maintain an existing React 16, 17, or 18 application already storing Quill 1 Delta documents
  • You need a low-risk patch to a working ReactQuill v2 integration and cannot migrate editor data or toolbar code now
  • You depend on ReactQuill's component ref and callback shape and have tests covering its hybrid controlled behavior
Skip it if

Setup reality

Install react-quill beside supported React and React DOM versions, then import a Quill theme stylesheet or toolbars and popups will look broken. The package pins the old Quill 1 line and does not declare React 19 support. Browser-only code needs client-side or dynamic loading in SSR frameworks. You must also choose HTML versus Delta storage, avoid feeding change-only Deltas back as value, sanitize HTML at trust boundaries, and keep module configuration stable across renders.

Patterns

Use the hybrid controlled HTML modecontrolled-html-editor

import { useState } from 'react'
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'

export function Editor() {
  const [html, setHtml] = useState('')
  return <ReactQuill theme="snow" value={html} onChange={setHtml} />
}

ReactQuill cannot prevent Quill's DOM edit first, so value can overwrite content afterward; it is not a normal fully controlled input.

Avoid repeated content reconciliationuncontrolled-editor

<ReactQuill
  theme="snow"
  defaultValue="<p>Draft text</p>"
  onChange={html => saveDraft(html)}
/>

defaultValue is read only during initialization. Later prop changes do not reset the document.

Store the complete Delta documentstore-full-delta

const [document, setDocument] = useState({ ops: [] })

<ReactQuill
  value={document}
  onChange={(_html, _change, _source, editor) => {
    setDocument(editor.getContents())
  }}
/>

Never pass the second onChange argument back as value; it contains only the last change and can cause an update loop.

Limit toolbar controls and accepted formatslimit-formats

const modules = {
  toolbar: [
    [{ header: [1, 2, false] }],
    ['bold', 'italic', 'underline'],
    [{ list: 'ordered' }, { list: 'bullet' }],
    ['link', 'clean'],
  ],
}
const formats = ['header', 'bold', 'italic', 'underline', 'list', 'bullet', 'link']

<ReactQuill modules={modules} formats={formats} />

Formats omitted from formats are not accepted even if a toolbar button or pasted content tries to apply them.

Use the imperative Quill instanceaccess-quill-instance

import { useRef } from 'react'

const quillRef = useRef(null)

function focusEditor() {
  quillRef.current?.getEditor().focus()
}

<ReactQuill ref={quillRef} defaultValue="" />

getEditor exposes the mutable Quill instance. Prefer the callback editor proxy for reads and use the full instance only when props cannot express the operation.

Track selection changesread-selection

<ReactQuill
  onChangeSelection={(range, source, editor) => {
    if (range) {
      console.log(range.index, range.length, source, editor.getText(range.index, range.length))
    }
  }}
/>

range is null when the editor loses focus, so guard it before reading index or length.

Switch to read-only modetoggle-read-only

<ReactQuill
  theme="snow"
  value={html}
  readOnly={!canEdit}
  modules={{ toolbar: canEdit }}
/>

readOnly disables editing, but your application must still enforce authorization when saving content.

Add a custom toolbar actionadd-toolbar-handler

function insertDivider() {
  const range = this.quill.getSelection(true)
  this.quill.insertText(range.index, '\n---\n', 'user')
}

const modules = {
  toolbar: {
    container: [['bold', 'italic'], ['divider']],
    handlers: { divider: insertDivider },
  },
}

Use a normal function when the handler needs this.quill; an arrow function does not receive the toolbar module's this value.

Load ReactQuill only in a Next.js browserload-without-ssr

import dynamic from 'next/dynamic'

const ReactQuill = dynamic(() => import('react-quill'), { ssr: false })

export function ClientEditor(props) {
  return <ReactQuill theme="snow" {...props} />
}

This avoids evaluating the DOM-dependent editor on the server; import the theme CSS from a location your Next.js setup allows.

Read plain text separately from HTMLget-plain-text

<ReactQuill
  onChange={(html, _delta, _source, editor) => {
    save({ html, text: editor.getText() })
  }}
/>

Quill's plain text generally includes a trailing newline; normalize it only if your storage or character-count rules require that.

Alternatives

PackageRegistryPick it when
quillnpmYou want current Quill 2 and can follow its official direct React integration without a wrapper
react-quill-newnpmYou need a community-maintained ReactQuill-style fork with newer React and Quill compatibility
@tiptap/reactnpmYou want an actively developed headless editor with deeper extension and schema control