Home

Awesome

ngx-quill Build Status

<img src="https://raw.githubusercontent.com/KillerCodeMonkey/ngx-quill/master/ngx-quill-logo-v2.png" width="200px">

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

<table> <thead> <tr> <th>Angular</th> <th>ngx-quill</th> <th>supported</th> </tr> </thead> <tbody> <tr> <td> v19 </td> <td> >= 27.0.0 (quill v2) </td> <td> until May, 2025 </td> </tr> <tr> <td> v18 </td> <td> 26.x (quill v2) </td> <td> until Nov, 2025 </td> </tr> <tr> <td> v17.1 </td> <td> 25.x (quill v2) </td> <td> until May, 2025 </td> </tr> </tbody> </table>

Examples

Installation

@import '~quill/dist/quill.bubble.css';
// or
@import '~quill/dist/quill.snow.css';

For standard webpack, angular-cli and tsc builds

import { QuillModule } from 'ngx-quill'

@NgModule({
  imports: [
    ...,

    QuillModule.forRoot()
  ],
  ...
})
class YourModule { ... }

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

Custom Modules and options/formats

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

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
  ]
};
// 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$);
}
// Example with registering custom fonts
customOptions: [{
  import: 'formats/font',
  whitelist: ['mirza', 'roboto', 'aref', 'serif', 'sansserif', 'monospace']
}]
// 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$ }
]

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>

Full Quill Toolbar HTML

Outputs

editor // Quill
{
  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)
}
{
  editor: editorInstance, // Quill
  range: range, // Range
  oldRange: oldRange, // Range
  source: source // ('user', 'api', 'silent' , undefined)
}
{
  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)
}
{
  editor: editorInstance, // Quill
  source: source // ('user', 'api', 'silent' , undefined)
}
{
  editor: editorInstance, // Quill
  source: source // ('user', 'api', 'silent' , undefined)
}
{
  editor: editorInstance, // Quill
  source: source // ('dom')
}
{
  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

Outputs

<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:

  1. 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)
  2. When using classes use a div-tag that has the innerHTML attribute and add the ql-editor class. Wrap your div in another div-tag with css classes ql-container and your theme, e.g. ql-snow.:
  3. 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>
  1. 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:

<quill-view-html [content]="htmlstring" theme="snow"></quill-view-html>

Config

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.