npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ebdev/ngx-richtext

v0.4.1

Published

Professional Angular rich text editor with free and premium feature tiers

Readme

ngx-richtext

Professional Angular rich text editor — one tag, toolbar included, HTML value format, host-delegated image uploads, and free/premium feature tiers unlocked by an offline signed license key.

Live demo → https://ngx-richtext.ebdev-design.com

Install

npm install ngx-richtext

Peer dependencies: @angular/core, @angular/common, @angular/forms (v18, v19, v20 or v21) and rxjs.

Import styles once in your global stylesheet:

@import 'ngx-richtext/styles.css';

(After install, the stylesheet is exported as ngx-richtext/styles.css.)

Quick start

import { signal } from '@angular/core';
import { provideRichTextEditor, RteEditorComponent } from 'ngx-richtext';

export const appConfig = {
  providers: [
    provideRichTextEditor({
      // optional premium license
      // licenseKey: '...',
      imageUploadHandler: (file) => myApi.upload(file), // returns Promise<string> | Observable<string>
    }),
  ],
};

readonly html = signal('');

Add RteEditorComponent to the component's imports, then drop in a single tag:

<ngx-rte [(value)]="html" placeholder="Type here…" />

That's the whole editor — toolbar included. The [(value)] model is free.

Reactive forms (Premium)

With a premium license (reactiveForms), bind a FormControl for validation, disable(), and touched/dirty status:

import { FormControl, Validators } from '@angular/forms';

readonly body = new FormControl('', {
  nonNullable: true,
  validators: [Validators.required],
});
<ngx-rte [formControl]="body" placeholder="Type here…" />
<!-- or formControlName="body" inside a [formGroup] -->

An empty editor emits '', so Validators.required (and other string validators) behave as expected. Without a license, FormControl binding is ignored (with a console warning) and [(value)] keeps working.

Toolbar on / off

The toolbar renders by default. Set showToolbar to false for a plain editable surface:

<ngx-rte showToolbar="false" [(value)]="html" />
<ngx-rte [showToolbar]="!readonlyMode()" [(value)]="html" />

Override which controls it shows with toolbarItems (or globally via the toolbar config):

<ngx-rte [toolbarItems]="['bold', 'italic', '|', 'link']" [(value)]="html" />

Toolbar outside the editor

To place the toolbar somewhere else, turn the built-in one off and bind <ngx-rte-toolbar> to the editor:

<ngx-rte-toolbar [editor]="editor" />
<ngx-rte #editor showToolbar="false" [(value)]="html" />

The editor's value model signal is a sanitized HTML string (free). ControlValueAccessor / FormControl support is a premium feature (reactiveForms).

RTL / Hebrew & Arabic

Use the LTR / RTL toolbar buttons to set writing direction on the current block (or list). HTML is stored with dir="rtl" (and style="direction: rtl").

You can also set a default for the whole editor:

<ngx-rte [dir]="'rtl'" [(value)]="html" />

Tables

The table button opens a picker with two ways to choose a size: hover the grid for quick sizes, or type exact row and column counts for anything larger. A Header row option puts the first row in a <thead> as <th> cells.

Once the caret is inside a table, the toolbar shows controls to insert a row above or below, insert a column left or right, and delete the current row, column, or the whole table — so a table can grow to any size after it is created.

Limits and picker defaults are configurable:

provideRichTextEditor({
  table: {
    maxRows: 50,        // largest table the picker and inputs accept
    maxColumns: 20,
    pickerRows: 10,     // size of the hover grid
    pickerColumns: 10,
    headerRow: true,    // default state of the "Header row" checkbox
  },
})

Sizes typed into the inputs are clamped to maxRows / maxColumns.

Image uploads

The package never uploads to a server itself. Provide imageUploadHandler in provideRichTextEditor:

imageUploadHandler: (file: File) => this.http.post<{url: string}>('/api/images', formData).pipe(map(r => r.url))

Flow:

  1. User picks / pastes / drops an image
  2. Editor inserts a temporary placeholder (blob: URL + uploading marker)
  3. Your handler saves the file and returns the public URL
  4. Editor swaps src to that URL (or removes the placeholder and emits imageUploadError on failure)

Without a handler, the toolbar still exposes the image button and emits (imageRequest) with the File.

Free vs premium

| Feature | Free | Premium | | --- | --- | --- | | Bold / italic / underline / strike | ✓ | ✓ | | Headings, lists, alignment, LTR/RTL direction, links | ✓ | ✓ | | Undo / redo, clear formatting | ✓ | ✓ | | Basic image insert | ✓ | ✓ | | Text & highlight colors | ✓ | ✓ | | Tables (picker, header row, row/column editing) | ✓ | ✓ | | [(value)] signal / two-way model | ✓ | ✓ | | FormControl / formControlName / ngModel | | ✓ | | HTML source view | | ✓ | | Word / char counter | | ✓ | | Fullscreen | | ✓ | | Image resize helpers | | ✓ |

Optional modules are also available as secondary entry points so apps only bundle what they use:

import { createTableHtml } from 'ngx-richtext/tables';
import { countWords } from 'ngx-richtext/word-count';
import { enableImageResize } from 'ngx-richtext/image-resize';

License keys

Offline Ed25519-signed keys — no license server required. Every key is bound to the hostnames it was issued for and carries an expiry date (one year by default):

{
  "product": "@ebdev/ngx-richtext",
  "kid": "rte-2026-08",
  "plan": "premium",
  "features": ["imageResize", "sourceView", "wordCount", "fullscreen", "reactiveForms"],
  "domains": ["acme.com", "*.acme.com"],
  "expiry": "2027-08-03",
  "licensee": "Acme Inc"
}

Pass the key to provideRichTextEditor({ licenseKey }). Verification happens once at startup and never blocks rendering — a missing, malformed, expired, or wrong-domain key logs a warning and falls back to the free tier.

Domain binding. *.acme.com matches the apex and every subdomain; notacme.com does not. localhost, 127.0.0.1, and *.localhost are always accepted so ng serve, CI, and unit tests work without a key of their own. Server rendering (no location) is likewise treated as a development host.

Expiry. When a key expires the editor keeps working — only premium features switch off. Renew by dropping in a new key; no code change needed. The stamped date is honoured for the whole of that day in every timezone.

Works without WebCrypto Ed25519. Verification prefers the browser's native implementation and falls back to a bundled pure-JS one where that is missing — older Chrome, enterprise-pinned builds, Android WebViews, and any page served over plain http://. The fallback is loaded on demand, so it costs nothing on browsers that do not need it.

Diagnosing a key. LicenseService.getState().reason is one of missing-key, parse-error, no-crypto, invalid-signature, product-mismatch, expired, or domain-mismatch. Each is also logged once with an explanation. no-crypto means the environment could not verify anything — it is never a problem with the key.

Issuing keys (maintainers):

RTE_LICENSE_PRIVATE_KEY=<private> node tools/generate-license.mjs \
  --licensee "Acme Inc" --domains "acme.com,*.acme.com"

Theming

Override CSS custom properties:

.ngx-rte {
  --rte-accent: #7c3aed;
  --rte-radius: 12px;
}

Use [theme]="'dark'" on <ngx-rte>, or add the class ngx-rte-theme-dark to any ancestor.

API overview

provideRichTextEditor(config)

  • licenseKey?: string
  • imageUploadHandler?: (file: File) => Observable<string> | Promise<string>
  • toolbar?: ToolbarItem[]
  • extraFeatures?: RteFeatureId[]
  • placeholder?: string

<ngx-rte>

Models: value (free)
Inputs: placeholder, disabled, minHeight, dir, showToolbar (default true), toolbarItems, theme
Outputs: contentChange, focused, blurred, imageUploadError, imageRequest, selectionStateChange
Premium reactiveForms: [formControl] / formControlName / ngModel. Empty content is '' so Validators.required works.

<ngx-rte-toolbar>

Only needed to place the toolbar outside the editor. Inputs: editor (required), items (optional toolbar layout)

<ngx-rte-wrapper>

Deprecated. <ngx-rte> is a complete card on its own. The old three-tag layout still works, but new code should use <ngx-rte> alone.

Development

npm start          # demo app
npm test           # unit tests
npm run e2e        # Playwright
npm run build      # library
npm run publish:dry

License

Source-available under the ngx-richtext License Agreementnot MIT.

  • Free tier — free forever, in unlimited applications, including commercial ones. No key, no registration.
  • Premium tier — requires a purchased license key, bound to your domains and renewed annually.
  • Source — published to read, audit, debug, and contribute to. Redistributing the library itself, or bypassing the feature gate, is not permitted.

Get a license →