
ngx-quill is an angular (>=2) module for the Quill 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 |
|---|---|---|
| v22 | >= 31.x | until Nov, 2027 |
| v21 | 29.x, 30.x | until May, 2027 |
| v20 | 28.x | until Nov, 2026 |
quill-view and quill-view-html componentnpm install ngx-quill@angular/core, @angular/common, @angular/forms, @angular/platform-browser, quill version ^2.0.0 and rxjs - peer dependencies of ngx-quillnode_modules/quill/dist), or add them in your css/scss files with @import statements, or add them external stylings in your build process.@import '~quill/dist/quill.bubble.css';
// or
@import '~quill/dist/quill.snow.css';
QuillModule from ngx-quill:import { QuillModule } from 'ngx-quill'
QuillModule to the imports of your NgModule:
@NgModule({
imports: [
...,
QuillModule.forRoot()
],
...
})
class YourModule { ... }
<quill-editor></quill-editor> in your templates to add a default quill editorHINT: 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.
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 :).
compositionend event from zone.js (https://angular.io/guide/zone#setting-up-zonejs)[!IMPORTANT] Currently there are many issues with HTML + Quill v2 (https://github.com/slab/quill/issues?q=is%3Aissue%20state%3Aopen%20getSemanticHtml especially https://github.com/slab/quill/issues/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
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.
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.
html, values: html | object | text | json, sets the model value type - html = html string, object = quill operation object, json = quill operation json, text = plain textconst 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
]
};
snowfalse, boolean (only for format="html")[styles]="{height: '250px'}"Insert text here ...document.body, pass 'self' to attach the editor elementinvalid and add ng-invalid classinvalid and add ng-invalid class, only set invalid if editor text not empty --> if you want to check if text is required --> use the required attributefalse[required]="true" - default: false, boolean expected (no strings!)falsefalse, possible values true | false | 'none'. Boosts performance when non html format is used (by avoiding expensive calls to quill.getSemanticHtml()). none skips all extra format data, so the event only returns the delta and oldDelta.// 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$);
}
{ 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']
}]
{ 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$ }
]
customOptions and customModules Demo Repo[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>
above
<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>
below
</quill-editor>
top, possible values top, bottomwarn, error, log or false to deactivate logging, default: warnuser (quill source user) or all content/selection changes should be trigger model update, default user. Using all is not recommended, it cause some unexpected sideeffects.onContentChanged, onEditorChanged, ngModel and form control value changes. Improves performance (especially when working with large, >2-3 MiB Deltas), as neither editorChangeHandler, nor textChangeHandler handler runs internally.$ claude mcp add ngx-quill \
-- python -m otcore.mcp_server <graph>