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

@advinsoftwares/ngx-rich-text-editor

v0.3.1

Published

A from-scratch Angular rich text editor (CuteEditor-style: formatting, tables, image/file manager, templates, HTML source view, find/replace) with zero required backend — pluggable persistence via DI tokens, IndexedDB/localStorage defaults included.

Readme

@advinsoftwares/ngx-rich-text-editor

A from-scratch Angular rich text editor built to cover the same ground as CuteEditor (CuteSoft RTE) — formatting toolbar, tables, an image/file manager, reusable templates, an HTML source view, and find/replace — without a commercial license or any required backend. The editing surface, toolbar, dialogs, undo history, and table logic are all hand-built on top of the browser's own contenteditable/Selection APIs; this is not a wrapper around TinyMCE/CKEditor/CuteEditor.

Compatible with Angular 16.2+ (peer dependencies on @angular/core, @angular/common, @angular/forms, and rxjs).

This README is the quick start. For the full API reference — every input/output, the ToolbarConfig flags, exactly how content flows in and out, the pluggable persistence services and their contracts, wiring a real backend, and multi-tenant/scalability notes — see GUIDE.md.

Install

npm install @advinsoftwares/ngx-rich-text-editor

Usage

import { RichTextEditorModule } from '@advinsoftwares/ngx-rich-text-editor';

@NgModule({
  imports: [RichTextEditorModule /* , ... */],
})
export class YourFeatureModule {}
<!-- Reactive forms -->
<rte-editor formControlName="bodyHtml"></rte-editor>

<!-- Template-driven -->
<rte-editor [(ngModel)]="bodyHtml"></rte-editor>

<rte-editor> implements Angular's ControlValueAccessor, so formControlName, [(ngModel)], validators, disabled state, and markAsTouched() all work exactly like they would on an <input>. The value is a plain HTML string.

Trim the toolbar per use case without a second component:

<rte-editor
  formControlName="comment"
  [toolbarConfig]="{ table: false, image: false, templates: false, fullscreen: false }"
  [minHeightPx]="120"
></rte-editor>

See the exported ToolbarConfig model for every flag.

What's included

Formatting (bold/italic/underline/strikethrough, font family/size, headings, alignment, lists, blockquote, colors), links, tables (insert + row/column add/delete + per-cell/per-table background color), an image manager (upload, gallery, insert, with an optional built-in downscale/WebP optimizer), a file attachment manager (upload any file type, insert as a clickable chip), a reusable template manager, an HTML source view, find & replace, undo/redo, an in-editor help guide, a fullscreen mode, and paste cleanup that preserves pasted visual formatting (including class-based CSS from Word/Excel/web sources).

Persistence for images, attachments, and templates is 100% pluggable via Angular DI tokens (IMAGE_ASSET_SERVICE, FILE_ATTACHMENT_SERVICE, TEMPLATE_SERVICE, all exported from this package), and ships with zero-config local defaults so it works standalone with no server: images/attachments in IndexedDB, templates in localStorage. Override any of the three tokens in your own module to point at a real backend (a suggested .NET Core Web API + SQL Server + Dapper contract, matching this package's default service interfaces, is documented in the source repository's BACKEND-INTEGRATION.md).

Pasting images directly (screenshots, clipboard images)

Copying a screenshot and pasting it straight into the editor (Ctrl+V, not the toolbar's Insert Image button) embeds it as a base64 data: URI right inside the saved HTML, rather than going through IImageAssetService. That's a deliberate choice: a directly-pasted image has no natural "file" to upload and no dialog to go through, so embedding it makes the pasted image travel with the document to any device or user with zero backend and zero local browser storage involved — nothing to look up, nothing that can go missing.

The tradeoff is size: base64 inflates the image by roughly a third, and because it's part of the document's own HTML, it's included in every undo snapshot too (see EditorHistoryService's note on historyLimit for what that costs at scale). For a document with a handful of pasted screenshots this is a non-issue; for one with dozens of large, high-resolution pastes, consider trimming [historyLimit] lower, or asking users to use the Image Manager's Upload button instead (which always uses a lightweight URL reference, never inlines bytes).

Images inserted via the toolbar's Insert Image button are unaffected by this — those always go through IImageAssetService and use a real, lightweight reference (an IndexedDB-backed object URL by default, or a permanent server URL if you've wired up HttpImageAssetService below), never base64.

Resizing images

Click any image in the surface — pasted-as-base64 or inserted via the toolbar, both are handled identically — to select it; four small square handles appear on its corners. Dragging a handle resizes the image with its aspect ratio locked (undistorted resize, the same default most document editors use for images; free distortion isn't offered since it's rarely what's actually wanted). The resize is clamped to the image's own containing element (its parent — a table cell, if that's where it lives — not just the overall editor), and to a 24px floor so it can never be dragged down to invisible.

The new size is written directly onto the <img> as an inline style="width:…px;height:…px" (replacing any width/height attributes the toolbar's Image Manager may have set at insert time, so the element never carries two conflicting size hints), which means it's a completely normal part of the saved HTML — it survives reload, travels with the document to any device, and participates in undo/redo exactly like a formatting change or a paste does. Clicking anywhere else deselects the image and hides the handles; the handles themselves are never part of the saved content (they're built and positioned as a plain DOM overlay outside the contenteditable tree, not inserted into it), so there's nothing to strip out before saving.

Disabled entirely when [readOnly]/disabled — a read-only document shouldn't let its images be dragged around.

Wiring uploads to a real endpoint (HttpImageAssetService / HttpFileAttachmentService)

The toolbar's Insert Image and Insert Attachment dialogs both call upload(file) on whatever service is provided for IMAGE_ASSET_SERVICE / FILE_ATTACHMENT_SERVICE. Alongside the zero-config IndexedDB defaults, this package also exports HttpImageAssetService and HttpFileAttachmentService — ready-made implementations that POST the file to a configurable endpoint and use whatever URL the server hands back directly as the <img src> / attachment href. This is the same "upload once, get back a permanent URL" pattern used by CuteEditor and most real-world document/image upload APIs — no base64, no local browser storage, and it works across every device and user because the file genuinely lives on a server.

import { HttpClientModule } from '@angular/common/http';
import {
  RichTextEditorModule,
  IMAGE_ASSET_SERVICE,
  HttpImageAssetService,
  RTE_IMAGE_UPLOAD_ENDPOINT,
  FILE_ATTACHMENT_SERVICE,
  HttpFileAttachmentService,
  RTE_FILE_UPLOAD_ENDPOINT,
} from '@advinsoftwares/ngx-rich-text-editor';

@NgModule({
  imports: [HttpClientModule, RichTextEditorModule /* , ... */],
  providers: [
    { provide: IMAGE_ASSET_SERVICE, useClass: HttpImageAssetService },
    { provide: RTE_IMAGE_UPLOAD_ENDPOINT, useValue: 'https://api.example.com/api/editor/images' },
    { provide: FILE_ATTACHMENT_SERVICE, useClass: HttpFileAttachmentService },
    { provide: RTE_FILE_UPLOAD_ENDPOINT, useValue: 'https://api.example.com/api/editor/attachments' },
  ],
})
export class YourFeatureModule {}

Each endpoint is expected to behave like BACKEND-INTEGRATION.md's suggested API surface: POST <endpoint> with the file as multipart/form-data returns the asset's JSON (id, fileName, url, sizeBytes, contentType, createdAtUtc); GET <endpoint> lists existing assets the same shape; DELETE <endpoint>/{id} removes one. You must import HttpClientModule (or call provideHttpClient()) somewhere in your app yourself — this library doesn't import it for you, to avoid the well-known pitfall of registering HttpClientModule's providers more than once across a module tree.

License

MIT