Angular Rich Text Editor: Install and Configure
Scryb is a rich text editor built on Tiptap and ProseMirror. The Angular
adapter — @scryb-editor/angular — ships a full-featured standalone component with toolbar,
bubble menus, slash commands, side menu, and image upload built in.
Prerequisites
Section titled “Prerequisites”- Angular 20 or higher (
@scryb-editor/angulardeclares>=20.0.0 <23.0.0for@angular/core,@angular/common, and@angular/forms) - Node.js 24 or higher
- A working Angular application (
ng newor existing project) @scryb-editor/angularis commercially licensed; a subscription provides the signed key (see Licensing)
Installation
Section titled “Installation”npm install @scryb-editor/angular @scryb-editor/core @scryb-editor/extensions @scryb-editor/themespnpm add @scryb-editor/angular @scryb-editor/core @scryb-editor/extensions @scryb-editor/themesyarn add @scryb-editor/angular @scryb-editor/core @scryb-editor/extensions @scryb-editor/themesImport CSS
Section titled “Import CSS”The editor styles must be loaded globally — they cannot be scoped inside a single component. Choose one of the two methods below.
Option A: angular.json styles array (recommended)
Section titled “Option A: angular.json styles array (recommended)”Open angular.json and add the stylesheet to your application’s styles array:
{ "projects": { "my-app": { "architect": { "build": { "options": { "styles": [ "node_modules/@scryb-editor/themes/dist/all.css", "src/styles.css" ] } } } } }}Option B: global styles.css @import
Section titled “Option B: global styles.css @import”Add the import to your root styles.css (or styles.scss):
@import "@scryb-editor/themes/all";Basic Usage
Section titled “Basic Usage”Import ScrybEditorComponent into your standalone component and add the
<scryb-editor> selector to your template.
import { Component, signal } from "@angular/core";import { FormsModule } from "@angular/forms";import { ScrybEditorComponent } from "@scryb-editor/angular";import type { ScrybEditorConfig } from "@scryb-editor/core";
@Component({ selector: "app-my-editor", standalone: true, imports: [ScrybEditorComponent, FormsModule], template: ` <scryb-editor [(ngModel)]="content" [options]="editorConfig" (contentChange)="onContentChanged($event)" /> `,})export class MyEditorComponent { content = signal("");
readonly editorConfig: Partial<ScrybEditorConfig> = { placeholder: "Start writing...", };
onContentChanged(html: string): void { console.log("Editor content:", html); }}Configuration
Section titled “Configuration”All editor configuration is passed through the [options] input as a Partial<ScrybEditorConfig> object. Import the type from @scryb-editor/core.
Locale
Section titled “Locale”Display toolbar labels and tooltips in a specific language. Supported values:
"en" (default), "fr", "pt" (Brazilian Portuguese), "es", "zh" (Simplified Chinese).
readonly editorConfig: Partial<ScrybEditorConfig> = { locale: "fr",};Need a language beyond the five built-ins, or want to override a few strings without
forking a locale? See the Internationalization guide for registerLocale()
and config.translations.
Control light/dark appearance. Supported values: "light", "dark", "auto" (follows
prefers-color-scheme).
readonly editorConfig: Partial<ScrybEditorConfig> = { theme: "dark",};Toolbar configuration
Section titled “Toolbar configuration”Customize which toolbar items are shown and their order:
readonly editorConfig: Partial<ScrybEditorConfig> = { toolbar: { items: ["bold", "italic", "underline", "separator", "heading1"], show: true, },};<scryb-editor [options]="editorConfig" />Height configuration
Section titled “Height configuration”Control the minimum and maximum height of the editor area:
readonly editorConfig: Partial<ScrybEditorConfig> = { height: { minHeight: 200, maxHeight: 600, },};Placeholder
Section titled “Placeholder”Set the placeholder text shown when the editor is empty:
readonly editorConfig: Partial<ScrybEditorConfig> = { placeholder: "Write something amazing...",};Custom extensions
Section titled “Custom extensions”Everything on this page so far is configuration: a closed set of features the editor already
registers. For anything outside it — a custom node or mark, a ProseMirror plugin, an official
Tiptap extension this adapter does not configure — use the extensions input. It takes an array
of Tiptap extensions and appends them after the Scryb defaults.
import { Component } from "@angular/core";import { ScrybEditorComponent } from "@scryb-editor/angular";import type { AnyExtension } from "@tiptap/core";
import { MyCustomBlock } from "./my-custom-block";
@Component({ selector: "app-my-editor", standalone: true, imports: [ScrybEditorComponent], template: `<scryb-editor [extensions]="extensions" />`,})export class MyEditorComponent { readonly extensions: AnyExtension[] = [MyCustomBlock];}Because they are appended, an extension here extends the editor rather than displacing part of
it. The resulting order is Scryb defaults, then yours, then the adapter’s own — the same order
@scryb-editor/react produces from its extensions prop.
Opt-in features
Section titled “Opt-in features”Scryb ships a catalog of opt-in features gated by ScrybEditorConfig keys. Omitting a key leaves the corresponding Tiptap extension unregistered — zero runtime cost. The subsections below show the minimal config for each feature. typography and taskList are default-on (no config needed).
Autosave
Section titled “Autosave”Debounced save handler that fires on content change. <scryb-save-status> is auto-rendered in the editor footer when autosave is configured.
readonly editorConfig: Partial<ScrybEditorConfig> = { autosave: { saveFn: async (content) => { await fetch("/api/save", { method: "POST", body: JSON.stringify(content) }); }, debounceMs: 1000, format: "json", },};// <scryb-save-status> is auto-rendered in the editor footer.Table of Contents
Section titled “Table of Contents”Sidebar component with scroll-tracking. Requires uniqueId for stable heading IDs. Create a TocCallbacks store once and pass the same reference to both the config and <scryb-toc>.
import { ScrybEditorComponent, ScrybTocComponent } from "@scryb-editor/angular";import { createTocCallbackStore, type TocCallbacks } from "@scryb-editor/core";
@Component({ imports: [ScrybEditorComponent, ScrybTocComponent], template: ` <div class="layout"> <scryb-toc [editor]="editor" [callbacks]="tocCallbacks" /> <scryb-editor [options]="editorConfig" /> </div> `,})export class MyEditorComponent { readonly tocCallbacks: TocCallbacks = createTocCallbackStore(); readonly editorConfig: Partial<ScrybEditorConfig> = { uniqueId: { types: ["heading"] }, toc: { enabled: true, callbacks: this.tocCallbacks }, };}Mention
Section titled “Mention”@user typeahead popup. The popup is auto-mounted by ScrybEditorComponent — consumers only need to create the callback store and supply the item list.
import { createMentionCallbackStore } from "@scryb-editor/core";
readonly mentionCallbacks = createMentionCallbackStore();readonly editorConfig: Partial<ScrybEditorConfig> = { mention: { enabled: true, char: "@", items: [{ id: "u1", label: "Alice" }, { id: "u2", label: "Bob" }], callbacks: this.mentionCallbacks, },};:shortcode typeahead plus toolbar/bubble button with a popular-first cold-start picker. Popup is auto-mounted by ScrybEditorComponent.
import { createEmojiCallbackStore } from "@scryb-editor/core";
readonly emojiCallbacks = createEmojiCallbackStore();readonly editorConfig: Partial<ScrybEditorConfig> = { emoji: { enabled: true, callbacks: this.emojiCallbacks },};// Type ":" inside the editor to open the picker, or click the emoji toolbar button.Details
Section titled “Details”Collapsible toggle blocks (editor + viewer). Insert via the slash menu.
readonly editorConfig: Partial<ScrybEditorConfig> = { details: { enabled: true },};// Insert via slash menu → "Toggle" (Advanced group).YouTube
Section titled “YouTube”YouTube video embeds via slash command.
readonly editorConfig: Partial<ScrybEditorConfig> = { youtube: { enabled: true },};// Insert via slash menu → "YouTube video" (Media group).Word count
Section titled “Word count”Live word/character count widget. <scryb-word-count> is auto-rendered in the editor footer when position === "footer" (default).
readonly editorConfig: Partial<ScrybEditorConfig> = { wordCount: { display: "both", position: "footer" },};// <scryb-word-count> is auto-rendered in the editor footer.Invisible characters
Section titled “Invisible characters”Toggle pilcrow/space/tab glyphs from the toolbar. Add the invisibleCharacters toolbar item to expose the toggle.
readonly editorConfig: Partial<ScrybEditorConfig> = { invisibleCharacters: { enabled: true }, toolbar: { items: ["bold", "italic", "separator", "invisibleCharacters"] },};Unique IDs
Section titled “Unique IDs”Stable id attributes on listed node types — required by toc for deep-link anchors.
readonly editorConfig: Partial<ScrybEditorConfig> = { uniqueId: { types: ["heading"] }, // default; also required by `toc`.};Default-on features
Section titled “Default-on features”typography (smart quotes, dashes, ellipsis, arrows, fractions) and taskList (interactive checkbox lists) are enabled by default. Pass typography: { enabled: false } to disable typography transformations for legal/code-heavy content. taskList has no config key.
Theming
Section titled “Theming”@scryb-editor/themes uses CSS custom properties under the .scryb-editor container class
(automatically applied by ScrybEditorComponent). Set the theme via the [options] config:
readonly editorConfig: Partial<ScrybEditorConfig> = { theme: "auto", // follows system preference};You can override individual design tokens in your global stylesheet:
.scryb-editor { --scryb-toolbar-bg: #f8f9fa; --scryb-button-active-bg: #e9ecef; --scryb-border-color: #dee2e6;}Granular CSS imports
Section titled “Granular CSS imports”If you prefer to load only the styles you need:
/* Only design tokens */@import "@scryb-editor/themes/tokens";
/* Only theme color schemes (light/dark) */@import "@scryb-editor/themes/themes";
/* Only ProseMirror content styles */@import "@scryb-editor/themes/content";Complete Example
Section titled “Complete Example”A fully configured editor component with locale, theme, and reactive form binding:
import { Component, signal } from "@angular/core";import { FormsModule } from "@angular/forms";import { ScrybEditorComponent } from "@scryb-editor/angular";import type { ScrybEditorConfig } from "@scryb-editor/core";
@Component({ selector: "app-editor-page", standalone: true, imports: [ScrybEditorComponent, FormsModule], template: ` <scryb-editor [(ngModel)]="content" [options]="editorConfig" (contentChange)="onContentChanged($event)" (editorReady)="onEditorReady()" /> <p>Characters: {{ charCount() }}</p> `,})export class EditorPageComponent { content = signal(""); charCount = signal(0);
readonly editorConfig: Partial<ScrybEditorConfig> = { locale: "en", theme: "auto", placeholder: "Start writing...", height: { minHeight: 300, maxHeight: 800 }, characterCount: { show: true }, toolbar: { show: true }, bubbleMenu: { show: true }, sideMenu: { enabled: true }, slashCommands: { enabled: true }, };
onContentChanged(html: string): void { this.content.set(html); }
onEditorReady(): void { console.log("Editor is ready"); }}Next Steps
Section titled “Next Steps”- Images and Uploads — Send images to your own storage instead of embedding them
- API Reference — Full reference for the Angular component API
- Migrating from angular-tiptap-editor — Upgrade guide from the legacy package
- Migrating to Unified Config — Upgrade from individual inputs to the
[options]pattern