Top Related Projects
Quill is a modern WYSIWYG editor built for compatibility and extensibility
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
A React framework for building text editors.
The next generation Javascript WYSIWYG HTML Editor.
Medium.com WYSIWYG editor clone. Uses contenteditable API to implement a rich text solution.
Quick Overview
ngx-quill is an Angular wrapper for the Quill rich text editor. It provides an easy-to-use component for integrating Quill into Angular applications, offering a wide range of customization options and features for creating powerful text editing experiences.
Pros
- Seamless integration with Angular projects
- Extensive configuration options for customizing the editor
- Regular updates and active maintenance
- Good documentation and community support
Cons
- Learning curve for advanced customizations
- Potential performance issues with large documents
- Limited mobile support for some features
- Dependency on the Quill library
Code Examples
- Basic usage:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [
QuillModule.forRoot()
]
})
export class AppModule { }
- Custom toolbar configuration:
<quill-editor [modules]="{ toolbar: [['bold', 'italic'], ['link', 'image']] }"></quill-editor>
- Two-way binding with ngModel:
<quill-editor [(ngModel)]="editorContent"></quill-editor>
- Handling editor events:
<quill-editor (onEditorCreated)="onEditorCreated($event)"
(onContentChanged)="onContentChanged($event)"
(onSelectionChanged)="onSelectionChanged($event)">
</quill-editor>
Getting Started
- Install the package:
npm install ngx-quill
- Import QuillModule in your app.module.ts:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [
QuillModule.forRoot()
]
})
export class AppModule { }
- Use the quill-editor component in your template:
<quill-editor></quill-editor>
- For custom configurations, add the necessary options:
<quill-editor [styles]="{ height: '200px' }"
[modules]="{ toolbar: [['bold', 'italic', 'underline']] }"
[(ngModel)]="editorContent">
</quill-editor>
Competitor Comparisons
Quill is a modern WYSIWYG editor built for compatibility and extensibility
Pros of Quill
- More comprehensive documentation and examples
- Broader community support and active development
- Offers additional features like custom formats and modules
Cons of Quill
- Larger bundle size due to more features
- Steeper learning curve for advanced customizations
- May require more configuration for specific use cases
Code Comparison
ngx-quill:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [QuillModule.forRoot()]
})
export class AppModule { }
Quill:
import Quill from 'quill';
const quill = new Quill('#editor', {
theme: 'snow'
});
Summary
While ngx-quill provides a more Angular-specific implementation, Quill offers a more flexible and feature-rich solution for various JavaScript frameworks. ngx-quill is easier to integrate into Angular projects, but Quill provides more customization options and a larger ecosystem. The choice between the two depends on the specific project requirements and the developer's familiarity with Angular.
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
Pros of TinyMCE
- More feature-rich and customizable out of the box
- Broader browser compatibility, including older versions
- Extensive documentation and community support
Cons of TinyMCE
- Larger file size and potentially slower load times
- Steeper learning curve for advanced customizations
- Commercial license required for some features
Code Comparison
TinyMCE initialization:
tinymce.init({
selector: 'textarea',
plugins: 'link image code',
toolbar: 'undo redo | bold italic | alignleft aligncenter alignright | code'
});
ngx-quill initialization:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [QuillModule.forRoot()]
})
export class AppModule { }
TinyMCE offers more configuration options directly in the initialization, while ngx-quill relies on Angular's module system for setup. TinyMCE provides a more traditional JavaScript approach, whereas ngx-quill is specifically designed for Angular integration, resulting in a more streamlined setup process for Angular applications.
Both editors offer powerful rich text editing capabilities, but TinyMCE generally provides more built-in features and customization options. ngx-quill, being a wrapper for Quill.js, offers a lighter-weight solution that may be preferable for simpler use cases or when working within the Angular ecosystem.
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
Pros of CKEditor 5
- More comprehensive and feature-rich out-of-the-box
- Better documentation and extensive API
- Larger community and ecosystem
Cons of CKEditor 5
- Heavier and potentially slower performance
- Steeper learning curve for customization
- Commercial licensing for some features
Code Comparison
CKEditor 5:
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
console.log(editor);
})
.catch(error => {
console.error(error);
});
ngx-quill:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [
QuillModule.forRoot()
]
})
export class AppModule { }
CKEditor 5 offers a more traditional JavaScript approach, while ngx-quill is specifically designed for Angular integration. CKEditor 5 provides more control over the editor instance, but ngx-quill offers simpler Angular-specific setup. Both libraries have their strengths, with CKEditor 5 being more feature-rich and customizable, while ngx-quill provides easier integration for Angular projects.
A React framework for building text editors.
Pros of Draft.js
- More flexible and customizable, allowing for complex text editing features
- Better support for collaborative editing and real-time updates
- Larger community and ecosystem, with more plugins and extensions available
Cons of Draft.js
- Steeper learning curve, especially for developers new to React
- Heavier bundle size, which may impact performance for smaller applications
- Less straightforward integration with Angular projects compared to ngx-quill
Code Comparison
Draft.js:
import { Editor, EditorState } from 'draft-js';
const [editorState, setEditorState] = useState(EditorState.createEmpty());
<Editor editorState={editorState} onChange={setEditorState} />
ngx-quill:
<quill-editor [(ngModel)]="editorContent"></quill-editor>
Draft.js provides a more programmatic approach to editor creation and state management, while ngx-quill offers a simpler, declarative integration with Angular's template syntax. Draft.js requires more setup but allows for greater control over the editor's behavior and appearance. ngx-quill, on the other hand, provides a more straightforward implementation for basic rich text editing needs in Angular applications.
The next generation Javascript WYSIWYG HTML Editor.
Pros of wysiwyg-editor
- More extensive features and plugins out-of-the-box
- Better documentation and support
- Customizable UI with themes and skins
Cons of wysiwyg-editor
- Commercial license required for use
- Larger file size and potentially slower performance
- Steeper learning curve for advanced customizations
Code Comparison
ngx-quill:
import { QuillModule } from 'ngx-quill';
@NgModule({
imports: [QuillModule.forRoot()]
})
wysiwyg-editor:
import { FroalaEditorModule, FroalaViewModule } from 'angular-froala-wysiwyg';
@NgModule({
imports: [FroalaEditorModule.forRoot(), FroalaViewModule.forRoot()]
})
Both libraries offer Angular integration, but wysiwyg-editor requires importing separate modules for editing and viewing. ngx-quill has a simpler setup with a single module import.
wysiwyg-editor provides more built-in features and a polished UI, making it suitable for projects requiring a feature-rich editor with minimal setup. However, it comes at the cost of a commercial license and potentially higher resource usage.
ngx-quill, being open-source and lightweight, is ideal for projects prioritizing customization and performance. It offers a good balance of features and flexibility but may require more effort to achieve the same level of functionality as wysiwyg-editor.
Medium.com WYSIWYG editor clone. Uses contenteditable API to implement a rich text solution.
Pros of medium-editor
- Lightweight and standalone, with no external dependencies
- Highly customizable with a wide range of options and extensions
- Supports inline editing and content-editable elements
Cons of medium-editor
- Not specifically designed for Angular integration
- Less active development and maintenance compared to ngx-quill
- May require additional work to integrate with modern frameworks
Code Comparison
medium-editor:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']
},
placeholder: {
text: 'Type your text'
}
});
ngx-quill:
<quill-editor [(ngModel)]="editorContent"
[modules]="{ toolbar: [['bold', 'italic', 'underline'], ['link']] }"
placeholder="Type your text">
</quill-editor>
The code comparison shows that medium-editor is initialized using JavaScript, while ngx-quill is implemented as an Angular component. ngx-quill provides a more straightforward integration with Angular's template syntax and two-way data binding. medium-editor offers more flexibility in terms of initialization and configuration but may require additional setup for Angular projects.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
ngx-quill
ngx-quill is an angular (>=2) module for the Quill Rich Text Editor containing all components you need.
Donate/Support
If you like my work, feel free to support it. Donations to the project are always welcomed :)
PayPal: PayPal.Me/bengtler
Compatibility to Angular Versions
Angular | ngx-quill | supported |
---|---|---|
v18 | >= 26.0.0 (quill v2) | until Nov, 2025 |
v17.1 | 25.x (quill v2) | until May, 2025 |
v17 | 24.x (quill v1) | until May, 2025 |
v16 | 23.x (quill v1) | until Nov, 2024 |
Examples
- 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
Installation
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';
For standard webpack, angular-cli and tsc builds
- import
QuillModule
fromngx-quill
:
import { QuillModule } from 'ngx-quill'
- add
QuillModule
to the imports of your NgModule:
@NgModule({
imports: [
...,
QuillModule.forRoot()
],
...
})
class YourModule { ... }
- 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 add QuillModule.forRoot()
to your imports in your root module to make sure the Config
services is registered.
Global Config
It's possible to set custom default modules and Quill config options with the import of the QuillConfigModule
from the ngx-quill/config
. This module provides a global config, but eliminates the need to import the ngx-quill
library into the vendor bundle:
import { QuillConfigModule } from 'ngx-quill/config';
@NgModule({
imports: [
...,
QuillConfigModule.forRoot({
modules: {
syntax: true,
toolbar: [...]
}
})
],
...
})
class AppModule {}
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 the syntax
module follow the Syntax Highlight Module Guide.
See Quill Configuration for a full list of config options.
The QuillModule
exports the defaultModules
if you want to extend them :).
Known issues
- IME/special characters can add some unwanted new line (https://github.com/KillerCodeMonkey/ngx-quill/issues/1821#issuecomment-2019331522) - 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 (https://github.com/KillerCodeMonkey/ngx-quill/issues/1547), because validation can only be done after quill editor is initialise - possible solution: /
Custom Modules and options/formats
- 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
Suppress global register warnings
Per default when Quill.register
is called and you are overwriting an already existing module, QuillJS logs a warning. If you pass customOptions
or customModules
ngx-quill is registering those modules/options/formats for you.
In e.g. an angular univeral project your AppModule
and so QuillModule.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 passing suppressGlobalRegisterWarning: true
.
QuillEditorComponent
Hint
Ngx-quill updates the ngModel or formControl for every user
change in the editor.
Checkout the QuillJS Source parameter of the text-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.
Config
- 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
const modules = {
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.ts
declare module '!!raw-loader!*.css' {
const css: string;
export default css;
}
// my.component.ts
const quillCSS$ = defer(() =>
import('!!raw-loader!quill/dist/quill.core.css').then((m) => {
const style = 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>',
})
export class MyComponent {
beforeRender = () => firstValueFrom(quillCSS$);
}
- use customOptions for adding for example custom font sizes - array of objects
{ import: string; whitelist: any[] }
--> this overwrites this options globally !!!
// Example with registering custom fonts
customOptions: [{
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 modules globally !!!
// 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:
const BlotFormatter$ = 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>
<div above-quill-editor-toolbar>
above
</div>
<div quill-editor-toolbar>
<span class="ql-formats">
<button class="ql-bold" [title]="'Bold'"></button>
</span>
<span class="ql-formats">
<select class="ql-align" [title]="'Aligment'">
<option selected></option>
<option value="center"></option>
<option value="right"></option>
<option value="justify"></option>
</select>
<select class="ql-align" [title]="'Aligment2'">
<option selected></option>
<option value="center"></option>
<option value="right"></option>
<option value="justify"></option>
</select>
</span>
</div>
<div below-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
Outputs
- 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, // Quill
html: html, // html string
text: text, // plain text string
content: content, // Content - operatins representation
delta: delta, // Delta
oldDelta: oldDelta, // Delta
source: source // ('user', 'api', 'silent' , undefined)
}
- onSelectionChanged - selection is updated, also triggered for onBlur and onFocus, because the selection changed
{
editor: editorInstance, // Quill
range: range, // Range
oldRange: oldRange, // Range
source: source // ('user', 'api', 'silent' , undefined)
}
- onEditorChanged - text or selection is updated - independent of the source
{
editor: editorInstance, // Quill
event: 'text-change' // event type
html: html, // html string
text: text, // plain text string
content: content, // Content - operatins representation
delta: delta, // Delta
oldDelta: oldDelta, // Delta
source: source // ('user', 'api', 'silent' , undefined)
}
or
{
editor: editorInstance, // Quill
event: 'selection-change' // event type
range: range, // Range
oldRange: oldRange, // Range
source: source // ('user', 'api', 'silent' , undefined)
}
- onFocus - editor is focused
{
editor: editorInstance, // Quill
source: source // ('user', 'api', 'silent' , undefined)
}
- onBlur - editor is blured
{
editor: editorInstance, // Quill
source: source // ('user', 'api', 'silent' , undefined)
}
- onNativeFocus - editor is focused, based on native focus event
{
editor: editorInstance, // Quill
source: source // ('dom')
}
- onNativeBlur - editor is blured, based on native blur event
{
editor: editorInstance, // Quill
source: source // ('dom')
}
QuillViewComponent, QuillViewHTMLComponent & How to present the editor content
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 the html
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.
QuillViewComponent - Using QuillJS to render 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 helper ngx-quill
provides a component where you can pass many options of the quill-editor
like modules, format, formats, customOptions, but renders only the content as readonly and without a toolbar. Import is the content
input, where you can pass the editor content you want to present.
Config
- 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 options globally !!!
- use customModules for adding and overwriting modules --> this overwrites this modules globally !!!
- sanitize - uses angulars DomSanitizer to sanitize html values - default:
false
, boolean (only for format="html")
Outputs
- onEditorCreated - editor instance
<quill-view [content]="content" format="text" theme="snow"></quill-view>
QuillViewHTMLComponent - Using angular [innerHTML] (DEPRECATED with quill v2)
Most of you will use the html
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. (https://github.com/slab/quill/issues/4103) (https://github.com/KillerCodeMonkey/ngx-quill/issues/1888)
<div class="ql-container ql-snow" style="border-width: 0;">
<div class="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 helper ngx-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>
Config
- content - html string to be presented
- theme - bubble/snow, default is
snow
- sanitize - default:
false
, boolean (uses DomSanitizer to bypass angular html sanitation when set to false)
Security Hint
Angular templates provide some assurance against XSS in the form of client side sanitizing of all inputs https://angular.io/guide/security#xss.
Ngx-quill components provide the input paramter sanitize
to sanitize html-strings passed as ngModel
or formControl
to the component.
It is deactivated per default to avoid stripping content or styling, which is not expected.
But it is recommended to activate this option, if you are working with html strings as model values.
Top Related Projects
Quill is a modern WYSIWYG editor built for compatibility and extensibility
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
A React framework for building text editors.
The next generation Javascript WYSIWYG HTML Editor.
Medium.com WYSIWYG editor clone. Uses contenteditable API to implement a rich text solution.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot