- Notifications
You must be signed in to change notification settings - Fork265
Angular (>=2) components for the Quill Rich Text Editor
License
KillerCodeMonkey/ngx-quill
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ngx-quill is an angular (>=2) module for theQuill Rich Text Editor containing all components you need.
If you like my work, feel free to support it. Donations to the project are always welcomed :)
PayPal:PayPal.Me/bengtler
Angular | ngx-quill | supported |
---|---|---|
v19 | >= 27.0.0 (quill v2) | until May, 2026 |
v18 | 26.x (quill v2) | until Nov, 2025 |
v17.1 | 25.x (quill v2) | until May, 2025 |
- Advanced Demo
- custom word count module
- custom toolbar with custom fonts and formats, toolbar position
- show the differences between sanitizing and not sanitizing your content if your content format is html
- usage of different content formats
- template-driven and reactive forms
- code + syntax highlighting
- formulas
- image resizing
- custom key-bindings, e.g. shift + b for bold
- dynamic styles and placeholder
- toggle readonly
- bubble toolbar
- activate formats after editor initialisation, e.g. rtl direction
- present quilljs content with the
quill-view
andquill-view-html
component
- Ionic Demo
- Angular Universal
npm install ngx-quill
- install
@angular/core
,@angular/common
,@angular/forms
,@angular/platform-browser
,quill
version^2.0.0
andrxjs
- peer dependencies of ngx-quill - include theme styling:bubble.css or snow.css of quilljs in your index.html (you can find them in
node_modules/quill/dist
), or add them in your css/scss files with@import
statements, or add them external stylings in your build process. - Example at the beginning of your style.(s)css:
@import'~quill/dist/quill.bubble.css';// or@import'~quill/dist/quill.snow.css';
- import
QuillModule
fromngx-quill
:
import{QuillModule}from'ngx-quill'
- add
QuillModule
to the imports of your NgModule:
@NgModule({imports:[ ...,QuillModule.forRoot()], ...})classYourModule{ ...}
- use
<quill-editor></quill-editor>
in your templates to add a default quill editor - do not forget to include quill + theme css in your buildprocess, module or index.html!
- for builds with angular-cli >=6 only add quilljs to your scripts or scripts section of angular.json, if you need it as a global :)!
HINT:If you are using lazy loading modules, you have to addQuillModule.forRoot()
to your imports in your root module to make sure theConfig
services is registered.
It's possible to set custom default modules and Quill config options with the import of theQuillConfigModule
from thengx-quill/config
. This module provides a global config, but eliminates the need to import thengx-quill
library into the vendor bundle:
import{QuillConfigModule}from'ngx-quill/config';@NgModule({imports:[ ...,QuillConfigModule.forRoot({modules:{syntax:true,toolbar:[...]}})], ...})classAppModule{}
Registering the global configuration can be also done using the standalone function if you are bootstrapping an Angular application using standalone features:
import{provideQuillConfig}from'ngx-quill/config';bootstrapApplication(AppComponent,{providers:[provideQuillConfig({modules:{syntax:true,toolbar:[...]}})]})
If you want to use thesyntax
module follow theSyntax Highlight Module Guide.
SeeQuill Configuration for a full list of config options.
TheQuillModule
exports thedefaultModules
if you want to extend them :).
- IME/special characters can add some unwanted new line (#1821 (comment)) - possible solution: unpatch the
compositionend
event from zone.js (https://angular.io/guide/zone#setting-up-zonejs) - formControl/model change is triggered on first rendering by quill (#1547), because validation can only be done after quill editor is initialise - possible solution: /
Important
Currently there are many issues with HTML + Quill v2 (https://github.com/slab/quill/issues?q=is%3Aissue%20state%3Aopen%20getSemanticHtml especiallyslab/quill#4509)
Tip
using html format is general not the best practise, use the "Delta"-represenation as object/json to be able to correclty render it and be able to migrate to different editors or major quill version
- use customOptions for adding for example custom font sizes or other options/formats
- use customModules for adding and overwriting modules, e.g. image-resize or your own modules
Per default whenQuill.register
is called and you are overwriting an already existing module, QuillJS logs a warning. If you passcustomOptions
orcustomModules
ngx-quill is registering those modules/options/formats for you.
In e.g. an angular univeral project yourAppModule
and soQuillModule.forRoot()
is executed twice (1x server side, 1x browser). QuillJS is running in a mocked env on server side, so it is intendet that every register runs twice.
To subpress those expected warnings you can turn them off by passingsuppressGlobalRegisterWarning: true
.
Ngx-quill updates the ngModel or formControl for everyuser
change in the editor.Checkout theQuillJS Source parameter of thetext-change
event.
If you are using the editor reference to directly manipulate the editor content and want to update the model, pass'user'
as the source parameter to the QuillJS api methods.
- ngModel - set initial value or allow two-way databinding for template driven forms
- formControl/formControlName - set initial value or allow two-way databinding for reactive forms
- readOnly (true | false) if user can edit content
- formats - array of allowed formats/groupings
- format - model format - default:
html
, values:html | object | text | json
, sets the model value type - html = html string, object = quill operation object, json = quill operation json, text = plain text - modules - configure/disable quill modules, e.g toolbar or add custom toolbar via html element default is
constmodules={toolbar:[['bold','italic','underline','strike'],// toggled buttons['blockquote','code-block'],[{'header':1},{'header':2}],// custom button values[{'list':'ordered'},{'list':'bullet'}],[{'script':'sub'},{'script':'super'}],// superscript/subscript[{'indent':'-1'},{'indent':'+1'}],// outdent/indent[{'direction':'rtl'}],// text direction[{'size':['small',false,'large','huge']}],// custom dropdown[{'header':[1,2,3,4,5,6,false]}],[{'color':[]},{'background':[]}],// dropdown with defaults from theme[{'font':[]}],[{'align':[]}],['clean'],// remove formatting button['link','image','video']// link and image, video]};
- theme - bubble/snow, default is
snow
- sanitize - uses angulars DomSanitizer to sanitize html values - default:
false
, boolean (only for format="html") - styles - set a styles object, e.g.
[styles]="{height: '250px'}"
- placeholder - placeholder text, default is
Insert text here ...
- bounds - boundary of the editor, default
document.body
, pass 'self' to attach the editor element - maxLength - add validation for maxlength - set model state to
invalid
and addng-invalid
class - minLength - add validation for minlength - set model state to
invalid
and addng-invalid
class, only set invalid if editor text not empty --> if you want to check if text is required --> use the required attribute - trimOnValidation - trim trailing|leading newlines on validation run for required, min- and maxLength, default
false
- required - add validation as a required field -
[required]="true"
- default: false, boolean expected (no strings!) - registry - custom parchment registry to not change things globally
- beforeRender - a function, which is executed before the Quill editor is rendered, this might be useful for lazy-loading CSS. Given the following example:
// typings.d.tsdeclare module'!!raw-loader!*.css'{constcss:string;exportdefaultcss;}// my.component.tsconstquillCSS$=defer(()=>import('!!raw-loader!quill/dist/quill.core.css').then((m)=>{conststyle=document.createElement('style');style.innerHTML=m.default;document.head.appendChild(style);})).pipe(shareReplay({bufferSize:1,refCount:true}));@Component({template:'<quill-editor [beforeRender]="beforeRender"></quill-editor>',})exportclassMyComponent{beforeRender=()=>firstValueFrom(quillCSS$);}
- use customOptions for adding for example custom font sizes - array of objects
{ import: string; whitelist: any[] }
--> this overwrites this optionsglobally !!!
// Example with registering custom fontscustomOptions:[{import:'formats/font',whitelist:['mirza','roboto','aref','serif','sansserif','monospace']}]
- use customModules for adding and overwriting modules - an array of objects
{ implementation: any; path: string }
--> this overwrites this modulesglobally !!!
// The `implementation` may be a custom module constructor or an Observable that resolves to// a custom module constructor (in case you'd want to load your custom module lazily).// For instance, these options are applicable:// import BlotFormatter from 'quill-blot-formatter';customModules=[{path:'modules/blotFormatter',implementation:BlotFormatter}]// Or:constBlotFormatter$=defer(()=>import('quill-blot-formatter').then(m=>m.default))customModules=[{path:'modules/blotFormatter',implementation:BlotFormatter$}]
- checkout the demo repo about usage of
customOptions
andcustomModules
Demo Repo - possibility to create a custom toolbar via projection slot
[quill-editor-toolbar]
and add content above[above-quill-editor-toolbar]
and below[below-quill-editor-toolbar]
the toolbar:
Try to not use much angular magic here, like(output)
listeners. Use native EventListeners
<quill-editor><divabove-quill-editor-toolbar> above</div><divquill-editor-toolbar><spanclass="ql-formats"><buttonclass="ql-bold"[title]="'Bold'"></button></span><spanclass="ql-formats"><selectclass="ql-align"[title]="'Aligment'"><optionselected></option><optionvalue="center"></option><optionvalue="right"></option><optionvalue="justify"></option></select><selectclass="ql-align"[title]="'Aligment2'"><optionselected></option><optionvalue="center"></option><optionvalue="right"></option><optionvalue="justify"></option></select></span></div><divbelow-quill-editor-toolbar> below</div></quill-editor>
- customToolbarPosition - if you are working with a custom toolbar you can switch the position :). - default:
top
, possible valuestop
,bottom
- debug - set log level
warn
,error
,log
orfalse
to deactivate logging, default:warn
- trackChanges - check if only
user
(quill source user) orall
content/selection changes should be trigger model update, defaultuser
. Usingall
is not recommended, it cause some unexpected sideeffects. - classes - a space separated list of CSS classes that will be added onto the editor element
- linkPlaceholder - optional - set placeholder for the link tooltip
- debounceTime - optional - debounces
onContentChanged
,onEditorChanged
,ngModel
and form control value changes. Improves performance (especially when working with large, >2-3 MiB Deltas), as neithereditorChangeHandler
, nortextChangeHandler
handler runs internally. - defaultEmptyValue - optional - change the default value for an empty editor. Currently it is
null
, but you can set it e.g. to empty string
- onEditorCreated - editor instance
- Use this output to get the editor instance and use it directly. After this output has called the component is stable and all listeners are binded
editor // Quill
- onContentChanged - text is updated
{editor:editorInstance,// Quillhtml:html,// html stringtext:text,// plain text stringcontent:content,// Content - operatins representationdelta:delta,// DeltaoldDelta:oldDelta,// Deltasource:source// ('user', 'api', 'silent' , undefined)}
- onSelectionChanged - selection is updated, also triggered for onBlur and onFocus, because the selection changed
{editor:editorInstance,// Quillrange:range,// RangeoldRange:oldRange,// Rangesource:source// ('user', 'api', 'silent' , undefined)}
- onEditorChanged - text or selection is updated - independent of the source
{editor:editorInstance,// Quillevent:'text-change'// event typehtml:html,// html stringtext:text,// plain text stringcontent:content,// Content - operatins representationdelta:delta,// DeltaoldDelta:oldDelta,// Deltasource:source// ('user', 'api', 'silent' , undefined)}
or
{editor:editorInstance,// Quillevent:'selection-change'// event typerange:range,// RangeoldRange:oldRange,// Rangesource:source// ('user', 'api', 'silent' , undefined)}
- onFocus - editor is focused
{editor:editorInstance,// Quillsource:source// ('user', 'api', 'silent' , undefined)}
- onBlur - editor is blured
{editor:editorInstance,// Quillsource:source// ('user', 'api', 'silent' , undefined)}
- onNativeFocus - editor is focused, based on native focus event
{editor:editorInstance,// Quillsource:source// ('dom')}
- onNativeBlur - editor is blured, based on native blur event
{editor:editorInstance,// Quillsource:source// ('dom')}
In most cases a wysiwyg editor is used in backoffice to store the content to the database. On the other side this value should be used, to show the content to the enduser.
In most cases thehtml
format is used, but it is not recommended by QuillJS, because it has the intention to be a solid, easy to maintain editor. Because of that it uses blots and object representations of the content and operation.
This content object is easy to store and to maintain, because there is no html syntax parsing necessary. So you even switching to another editor is very easy when you can work with that.
ngx-quill
provides some helper components, to present quilljs content.
In general QuillJS recommends to use a QuillJS instance to present your content.Just create a quill editor without a toolbar and in readonly mode. With some simple css lines you can remove the default border around the content.
As a helperngx-quill
provides a component where you can pass many options of thequill-editor
like modules, format, formats, customOptions, but renders only the content as readonly and without a toolbar. Import is thecontent
input, where you can pass the editor content you want to present.
- content - the content to be presented
- formats - array of allowed formats/groupings
- format - model format - default:
html
, values:html | object | text | json
, sets the model value type - html = html string, object = quill operation object, json = quill operation json, text = plain text - modules - configure/disable quill modules
- theme - bubble/snow, default is
snow
- debug - set log level
warn
,error
,log
orfalse
to deactivate logging, default:warn
- use customOptions for adding for example custom font sizes --> this overwrites this optionsglobally !!!
- use customModules for adding and overwriting modules --> this overwrites this modulesglobally !!!
- sanitize - uses angulars DomSanitizer to sanitize html values - default:
false
, boolean (only for format="html")
- onEditorCreated - editor instance
<quill-view[content]="content"format="text"theme="snow"></quill-view>
Most of you will use thehtml
format (even it is not recommended). To render custom html with angular you should use the[innerHTML]
attribute.
But there are some pitfalls:
- You need to have the quill css files loaded, when using classes and not inline styling (https://quilljs.com/guides/how-to-customize-quill/#class-vs-inline)
- When using classes use a
div
-tag that has theinnerHTML
attribute and add theql-editor
class. Wrap your div in anotherdiv
-tag with css classesql-container
and your theme, e.g.ql-snow
.: - With quill v2 ngx-quill is using
quill.getSemanticHTML()
to get html content. There some list tag information are stripped. (slab/quill#4103) (#1888)
<divclass="ql-container ql-snow"style="border-width: 0;"><divclass="ql-editor"[innerHTML]="byPassedHTMLString"></div></div>
- Angular has html sanitation, so it will strip unkown or not trusted parts of your HTML - just mark your html as trusted (DomSanitizer)
After that your content should look like what you expected.
If you store html in your database, checkout your backend code, sometimes backends are stripping unwanted tags as well ;).
As a helperngx-quill
provides a component where you can simply pass your html string and the component does everything for you to render it:
- add necessary css classes
- bypass html sanitation
<quill-view-html[content]="htmlstring"theme="snow"></quill-view-html>
- content - html string to be presented
- theme - bubble/snow, default is
snow
- sanitize - default:
false
, boolean (usesDomSanitizer to bypass angular html sanitation when set to false)
Angular templates provide some assurance against XSS in the form of client side sanitizing of all inputshttps://angular.io/guide/security#xss.
Ngx-quill components provide the input paramtersanitize
to sanitize html-strings passed asngModel
orformControl
to the component.
Caution
It isdeactivated per default to avoid stripping content or styling, which is not expected.
Tip
But it isrecommended to activate this option, if you are working with html strings as model values.
About
Angular (>=2) components for the Quill Rich Text Editor