
Syncfusion Angular Rich Text Editor
- 214 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-rich-text-editor for development tasks
About
syncfusion-angular-rich-text-editor: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-rich-text-editor
Syncfusion Angular Rich Text Editor by the numbers
- 214 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,879 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-rich-text-editorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 214 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-rich-text-editor for development tasks
Files
Implementing Syncfusion Angular Rich Text Editor
The Syncfusion Angular Rich Text Editor is a full-featured WYSIWYG editor that enables users to create, edit, and format rich text content. It supports HTML and Markdown editing modes, multimedia insertion, AI assistance, smart editing features, and deep customization.
When to Use This Skill
Use this skill when you need to:
- Content Editing UI: Add a rich text / WYSIWYG editor to an Angular application
- HTML or Markdown Output: Capture formatted content as HTML or Markdown
- Media-Rich Editors: Allow image, audio, video, and file insertion
- AI-Assisted Writing: Integrate AI content generation into the editor
- Smart Editing Features: Add mentions, emoji picker, slash commands, or mail merge
- Custom Toolbar: Configure, extend, or restrict toolbar items and tools
- Form Integration: Use RTE with Angular Reactive or Template-driven forms
- Document Import/Export: Import from Word or export to PDF/Word
Component Overview
The <ejs-richtexteditor> component from @syncfusion/ej2-angular-richtexteditor provides:
- HTML and Markdown editing modes
- Configurable toolbar (expand, multirow, scrollable, popup)
- Image, video, audio, and file insertion with upload support
- Inline, IFrame, and resizable editor variants
- Smart editing: Mentions, Emoji Picker, Slash Menu, Mail Merge
- AI assistant integration
- Import/Export (Word, PDF)
- Full accessibility (WCAG 2.1), keyboard navigation, RTL support
- Multiple themes: Tailwind 3, Material 3, Bootstrap 5, Fluent 2
Quick Start
import { Component } from '@angular/core';
import { RichTextEditorModule } from '@syncfusion/ej2-angular-richtexteditor';
import {
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor [(value)]="content"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public content: string = '<p>Start editing here...</p>';
}CSS (styles.css):
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/material3.css';Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
When to read: First-time setup, installation, basic implementation, module injection
Topics covered:
- Installing
@syncfusion/ej2-angular-richtexteditor - Module imports:
RichTextEditorModulevsRichTextEditorAllModule - Service injection: all available services and what they unlock
- CSS theme imports (Material3, Tailwind3, Bootstrap5, Fluent2)
- Standalone component vs NgModule setup
- Basic
<ejs-richtexteditor>with value binding
---
Editor Types
📄 Read: references/editor-types.md
When to read: Choosing the right editor rendering mode
Topics covered:
- Default (DIV-based) editor — standard use case
- IFrame editor — isolated styling, CSS encapsulation
- Inline editing — edit content directly in-page
- Resizable editor — user-resizable editor area
- Switching between HTML mode and Markdown mode
- When to choose each type
---
Toolbar Configuration
📄 Read: references/toolbar.md
When to read: Customizing toolbar layout, position, or items
Topics covered:
- Toolbar types: Expand, MultiRow, Scrollable, Popup (floating)
- Toolbar position: top or bottom
- Quick toolbar (image, link, table context toolbars)
- Configuring
toolbarSettings.itemsarray - Disabling or enabling toolbar items dynamically
- Toolbar with separators (
|) and line breaks
---
Built-in and Custom Tools
📄 Read: references/tools.md
When to read: Working with text formatting, custom toolbar items, or fullscreen
Topics covered:
- Built-in tools overview (all available tool names)
- Text formatting tools: Bold, Italic, FontName, FontSize, Color, Alignment
- Styling tools: Lists, Indent, Formats
- Fullscreen tool
- Creating custom toolbar tools with commands
- Styling custom tool buttons
---
Inserting Images, Video, Audio & Files
📄 Read: references/insert-image-media.md
When to read: Handling media insertion, uploads, or the file browser
Topics covered:
- Image insertion (URL, file upload, base64)
- Server-side upload handler configuration
- Image resize, alignment, and alt text
- Video insertion and embedding
- Audio insertion
- File browser integration (
FileManagerService) - Renaming images on server after upload
- Checking image size before insert
---
Tables and Links
📄 Read: references/table-and-links.md
When to read: Working with tables or hyperlinks inside the editor
Topics covered:
- Inserting and configuring tables
- Table resize, merge, split cells, add/remove rows and columns
- Table styles and borders
- Link insertion and editing
insertLinkdialog configuration- Opening links in new tab, link validation
---
Editor Value, Selection & Commands
📄 Read: references/editor-value-and-selection.md
When to read: Programmatically reading/writing content or controlling the editor
Topics covered:
- Getting and setting the HTML value
- Getting and setting Markdown value
executeCommandAPI (bold, italic, insertHTML, etc.)- Selection range APIs:
getRange,setRange - Positioning cursor at specific location
- Capturing keyboard shortcuts to update value
- Undo/redo programmatic control
---
Properties
📄 Read: references/properties.md
When to read: Configuring editor behavior, appearance, or feature settings
Topics covered:
- AI Assistant configuration (
aiAssistantSettings) - Auto-save settings (
autoSaveOnIdle) - Color palette configuration (
backgroundColor,fontColor) - Code block and format lists
- Editor mode settings (
editorMode,enterKey) - Emoji picker configuration
- Feature flags (enableAutoUrl, enableResize, enableXhtml, etc.)
- Import/Export settings (PDF, Word)
- IFrame and inline mode configuration
- Media insertion settings (image, audio, video)
- Styling properties (cssClass, height, htmlAttributes)
- Toolbar and format configuration
- File manager integration
---
Methods
📄 Read: references/methods.md
When to read: Calling programmatic APIs to control the editor
Topics covered:
- AI Assistant methods (
addAIPromptResponse,clearAIPromptHistory,executeAIPrompt,getAIPromptHistory) - Content retrieval methods (
getHtml,getText,getXhtml,getContent,getCharCount) - Content manipulation (
executeCommand,clearUndoRedo,sanitizeHtml) - Dialog management (
showDialog,closeDialog) - Focus management (
focusIn,focusOut) - Selection methods (
getRange,selectRange,selectAll,getSelection,getSelectedHtml) - Toolbar management (
enableToolbarItem,disableToolbarItem,removeToolbarItem) - Popup and UI control (
showEmojiPicker,showFullScreen,showSourceCode,showInlineToolbar,hideInlineToolbar,refreshUI) - Component lifecycle (
destroy)
---
Events — Core
📄 Read: references/events-core.md
When to read: Handling lifecycle, action, content change, selection, dialog, popup, quick toolbar, or resize events
Topics covered:
- Lifecycle events (
created,destroyed,focus,blur) - Action events (
actionBegin,actionComplete) - Content change events (
change) - Selection events (
selectionChanged) - Dialog events (
beforeDialogOpen,dialogOpen,beforeDialogClose,dialogClose) - Popup events (
beforePopupOpen,beforePopupClose) - Quick toolbar events (
beforeQuickToolbarOpen,quickToolbarOpen,quickToolbarClose) - Resize events (
resizeStart,resizing,resizeStop)
---
Events — Media, Upload & Toolbar
📄 Read: references/events-media-upload.md
When to read: Handling AI assistant, media, paste/clipboard, toolbar, upload, or import/export events
Topics covered:
- AI Assistant events (
aiAssistantPromptRequest,aiAssistantStopRespondingClick,aiAssistantToolbarClick) - Media events (
afterImageDelete,afterMediaDelete,beforeImageDrop,beforeMediaDrop) - Paste and clipboard events (
beforePasteCleanup,afterPasteCleanup,beforeClipboardWrite) - Toolbar events (
toolbarClick,updatedToolbarStatus,slashMenuItemSelect) - Upload events — image lifecycle (
beforeImageUpload,imageUploading,imageUploadSuccess,imageUploadFailed,imageSelected,imageRemoving) - Upload events — media lifecycle (
beforeFileUpload,fileUploading,fileUploadSuccess,fileUploadFailed,fileSelected,fileRemoving) - Document import/export events (
documentExporting,wordImporting)
---
Smart Editing Features
📄 Read: references/smart-editing.md
When to read: Adding mentions, emoji, slash commands, or mail merge
Topics covered:
- Mentions (
@user) — configuration and data source - Emoji picker — enabling and customizing
- Slash command menu — enabling
/trigger commands - Mail merge — inserting merge fields into content
- Combining multiple smart editing features
---
AI Assistant Integration
📄 Read: references/ai-assistant.md
When to read: Integrating AI content generation into the editor
Topics covered:
- AI AssistView integration overview
AIAssistantServiceinjection- Configuring AI properties (endpoint, model, prompts)
- Custom AI prompt handling in Angular
- Toolbar AI button setup
- AI content generation and insertion workflow
---
Clipboard and Paste Cleanup
📄 Read: references/clipboard-and-paste.md
When to read: Controlling what happens when content is pasted into the editor
Topics covered:
PasteCleanupService— enabling paste cleanup- Allowing/denying specific HTML tags and attributes
- Cleaning up Word and Excel paste formatting
ClipboardCleanupServicefor copy/cut cleanupafterPasteCleanupevent handling- Configuring paste as plain text
---
Styling and Accessibility
📄 Read: references/style-and-accessibility.md
When to read: Customizing editor appearance or ensuring accessibility compliance
Topics covered:
- CSS class customization and theming
- Style encapsulation in IFrame mode
- Custom placeholder styling
- WCAG 2.1 compliance overview
- Keyboard navigation and shortcuts
- ARIA attributes and screen reader support
- Focus management
---
Globalization and Localization
📄 Read: references/globalization-and-i18n.md
When to read: Supporting multiple languages, RTL, or locale-specific formatting
Topics covered:
- Localization setup with
L10n - RTL (Right-to-Left) support
- Custom locale string overrides
- Date and number formatting within the editor
---
Import, Export & Spell Check
📄 Read: references/import-export-spellcheck.md
When to read: Importing Word documents or exporting editor content, or adding spell/grammar check
Topics covered:
ImportExportServiceinjection- Importing from Word documents
- Exporting to PDF, HTML, or Word
- Spell check integration
- Grammar check integration
- Web Spell Checker plugin setup
---
Validation and Security
📄 Read: references/validation-and-security.md
When to read: Using the editor in forms or enforcing content security
Topics covered:
- Form validation with Angular Reactive forms
- Form validation with Template-driven forms
- XHTML validation
- Read-only mode
- Content sanitization
- XSS prevention strategies
---
Integrations and How-To
📄 Read: references/integrations-and-how-to.md
When to read: Integrating third-party libraries or solving common configuration scenarios
Topics covered:
- CodeMirror source-code editor integration
- HighlightJS syntax highlighting for code blocks
- Embedly rich media preview integration
- Tailwind CSS preflight conflict fix
- Adding Google Fonts to font picker
- File attachments support
- RTE inside Angular Dialog component
- RTE inside Angular Tab component
- Changing default font family
- Formatting code blocks
Common Patterns
Two-Way Value Binding
// Template
<ejs-richtexteditor [(value)]="htmlContent"></ejs-richtexteditor>
// Component
public htmlContent: string = '<p>Initial content</p>';One-Way Binding + Change Event
<ejs-richtexteditor [value]="htmlContent" (change)="onChange($event)">
</ejs-richtexteditor>
onChange(args: ChangeEventArgs): void {
this.htmlContent = args.value;
}Using with Reactive Forms
import { FormControl, ReactiveFormsModule } from '@angular/forms';
// Template
<ejs-richtexteditor formControlName="content"></ejs-richtexteditor>
// Component
this.form = new FormGroup({
content: new FormControl('<p></p>', Validators.required)
});Key Services Reference
| Service | What it enables |
|---|---|
ToolbarService | Toolbar rendering and item management |
HtmlEditorService | HTML editing mode |
MarkdownEditorService | Markdown editing mode |
LinkService | Hyperlink insertion and editing |
ImageService | Image insertion and management |
TableService | Table insertion and editing |
QuickToolbarService | Floating context toolbars |
FileManagerService | File browser dialog |
PasteCleanupService | Paste content sanitization |
ResizeService | Editor resizing handle |
ImportExportService | Word/PDF import and export |
AIAssistantService | AI assistant integration |
EmojiPickerService | Emoji picker dialog |
SlashMenuService | / slash command menu |
CodeBlockService | Inline code block formatting |
AudioService | Audio insertion |
VideoService | Video insertion |
FormatPainterService | Format painting (copy/apply styles) |
ClipboardCleanupService | Copy/cut clipboard cleanup |
AutoFormatService | Auto Markdown-to-HTML conversion |
Related Skills
- Syncfusion Angular Components — Parent library skill
AI Assistant Integration in Syncfusion Angular Rich Text Editor
Table of Contents
- Overview
- Setup and Styles
- Basic Integration
- Accessing the AI Assistant
- AI Assistant Properties
- Configuring AI Commands
- Popup Dimensions
- Preloading Prompts and Suggestions
- Conversation History
- Custom Toolbar Configuration
- Custom Toolbar Items
- Styling the AI Assistant Popup
- Handling AI Requests
- Streaming Responses
- Non-Streaming (Single) Response
- Stop Responding Event
- AI Toolbar Items Reference
---
Overview
The AI Assistant feature adds an AssistView popup to the Rich Text Editor, providing integrated AI capabilities for simplified content creation, editing, and enhancement. Users can:
- Apply predefined AI commands (Improve, Shorten, Elaborate, Simplify, Summarize, Grammar Check)
- Enter custom free-form prompts via the AI Query toolbar item
- Stream AI-generated responses into the editor for a typewriter-like effect
- Customize toolbars, popup dimensions, and AI interaction workflows
Requires:
AIAssistantServicein providersAICommandsand/orAIQueryin toolbar items- Additional style imports for interactive chat and notifications
The AI Assistant is accessed through two main toolbar items: 1. AICommands — Opens a predefined command menu 2. AIQuery — Opens a custom prompt input dialog (Alt+Enter / ⌥+Enter)
---
Setup and Styles
The Rich Text Editor AI Assistant requires additional style references beyond the standard RTE styles to render correctly. The Interactive Chat and Notifications styles are necessary for proper rendering of the AI AssistView.
Add the following style imports to src/styles.css:
@import '../node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/tailwind3.css';
/* AI Assistant — required */
@import '../node_modules/@syncfusion/ej2-interactive-chat/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-notifications/styles/tailwind3.css';---
Basic Integration
import { Component, ViewChild } from '@angular/core';
import {
RichTextEditorModule, RichTextEditorComponent, ToolbarSettingsModel,
ToolbarService, HtmlEditorService, LinkService, ImageService,
QuickToolbarService, AIAssistantService, AIAssistantPromptRequestArgs
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
#rte
[toolbarSettings]="tools"
(aiAssistantPromptRequest)="onAIRequest($event)">
</ejs-richtexteditor>
`,
providers: [
ToolbarService, HtmlEditorService, LinkService, ImageService,
QuickToolbarService, AIAssistantService
]
})
export class AppComponent {
@ViewChild('rte') editor!: RichTextEditorComponent;
public tools: ToolbarSettingsModel = {
items: [
'Bold', 'Italic', '|',
'AICommands', // Predefined AI commands (Improve, Shorten, etc.)
'AIQuery', // Custom prompt input (Alt+Enter shortcut)
'|', 'Undo', 'Redo'
]
};
async onAIRequest(args: AIAssistantPromptRequestArgs): Promise<void> {
// args.prompt — the selected AI command or user's custom prompt
// args.text — the currently selected text in the editor
// Call your AI service and respond with addAIPromptResponse()
await this.callAIService(args);
}
private async callAIService(args: AIAssistantPromptRequestArgs): Promise<void> {
// See Streaming or Non-Streaming sections below
}
}---
Accessing the AI Assistant
The AI Assistant interface can be opened through two main options:
AI Commands Menu
The AICommands toolbar option opens a menu containing predefined AI commands:
- Improve writing
- Shorten content
- Elaborate with detail
- Simplify language
- Summarize text
- Check grammar
AI Query
The AIQuery toolbar button or keyboard shortcut opens a popup for custom prompts:
- Windows: Alt + Enter
- Mac: ⌥ + Enter
---
AI Assistant Properties
The AIAssistantSettings class provides full customization via the following properties:
| Property | Type | Default | Description |
|---|---|---|---|
commands | AICommands[] | Predefined commands | Defines AI command options in the command dropdown menu |
popupMaxHeight | `string \ | number` | '400px' |
popupWidth | `string \ | number` | '600px' |
placeholder | string | 'Ask AI to rewrite or generate content.' | Placeholder text in the AI prompt textarea |
headerToolbarSettings | `(AssitantHeaderToolbarItems \ | IAIAssistantToolbarItem)[]` | ['AIcommands', 'Close'] |
promptToolbarSettings | `(AssistantPromptToolbarItems \ | IAIAssistantToolbarItem)[]` | ['Edit', 'Copy'] |
responseToolbarSettings | `(AssistantResponseToolbarItems \ | IAIAssistantToolbarItem)[]` | `['Regenerate', 'Copy', '\ |
prompts | PromptModel[] | [] | Collection of preloaded prompts and responses |
suggestions | string[] | [] | Suggestion prompts displayed to user as guidance |
bannerTemplate | `string \ | Function` | '' |
maxPromptHistory | number | 20 | Maximum number of stored prompts in history stack |
---
Configuring AI Commands
Customize the items displayed in the AI Commands dropdown menu using the commands property. This allows you to add custom prompts, create nested menu structures, and define AI interactions tailored to your application.
Example: Custom AI Commands with Nested Items
import { Component } from '@angular/core';
import { RichTextEditorModule, ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService, AIAssistantSettingsModel, ToolbarSettingsModel } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
templateUrl: 'app.component.html',
selector: 'app-root',
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService],
})
export class AppComponent {
public toolbarSettings: ToolbarSettingsModel = { items: ['AICommands', 'AIQuery'] };
public aiAssistantSettings: AIAssistantSettingsModel = {
commands: [
{ text: 'Rewrite', prompt: 'Rewrite the content to be more refined.' },
{ text: 'Elaborate', prompt: 'Expand on the following content with more detail and explanation:' },
{
text: 'Change Tone',
items: [
{ text: 'Professional', prompt: 'Rewrite the following content in a professional tone:' },
{ text: 'Casual', prompt: 'Rewrite the following content in a casual, conversational tone:' },
{ text: 'Direct', prompt: 'Rewrite the following content to be more direct and to the point:' },
],
},
]
};
}---
Popup Dimensions
Customize the size of the AI Assistant popup to fit your editor layout using the popupWidth and popupMaxHeight properties. The default minimum height is 100px.
Example: Custom Popup Size
public aiAssistantSettings: AIAssistantSettingsModel = {
popupWidth: 500, // Width in pixels
popupMaxHeight: 500 // Maximum height in pixels
};You can also use CSS values:
public aiAssistantSettings: AIAssistantSettingsModel = {
popupWidth: '80%', // 80% of editor width
popupMaxHeight: '50vh' // 50% of view height
};---
Preloading Prompts and Suggestions
Load conversation history and predefined suggestions using the prompts and suggestions properties. This is useful for returning users or initializing the AI Assistant with default guidance.
Example: Preloaded Conversations and Suggestions
public aiAssistantSettings: AIAssistantSettingsModel = {
prompts: [
{
prompt: 'What is Essential Studio ?',
response: 'Essential Studio is a software toolkit by Syncfusion that offers a variety of UI controls, frameworks, and libraries for developing applications on web, desktop, and mobile platforms. It aims to streamline application development with customizable, pre-built components.'
}
],
suggestions: [
'What are the popular components of Essential Studio?',
'Which web frameworks are supported by Essential Studio?'
]
};---
Conversation History
Track and manage conversation history using the maxPromptHistory property and getAIPromptHistory() method.
Setting Maximum History Length
Limit the number of stored prompts using maxPromptHistory (default: 20). The conversation is cleared when closing the popup.
Retrieving Conversation History
Use the getAIPromptHistory() method to retrieve all conversation history for saving or displaying previous chat sessions.
Example: Managing Conversation History
<ejs-richtexteditor
#editor
[aiAssistantSettings]="aiAssistantSettings">
</ejs-richtexteditor>
<br>
<button class="e-btn e-primary" (click)="onSaveBtnClick()"><span class="e-icons e-save"></span>Save</button>import { Component, ViewChild } from '@angular/core';
import { RichTextEditorModule, ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService, AIAssistantSettingsModel, ToolbarSettingsModel, RichTextEditorComponent } from '@syncfusion/ej2-angular-richtexteditor';
import { PromptModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [RichTextEditorModule],
standalone: true,
templateUrl: 'app.component.html',
selector: 'app-root',
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService],
})
export class AppComponent {
@ViewChild('editor')
public editor!: RichTextEditorComponent;
public toolbarSettings: ToolbarSettingsModel = { items: ['AICommands', 'AIQuery'] };
public aiAssistantSettings: AIAssistantSettingsModel = {
prompts: [
{
prompt: 'What is Essential Studio ?',
response: 'Essential Studio is a software toolkit by Syncfusion that offers a variety of UI controls, frameworks, and libraries for developing applications on web, desktop, and mobile platforms. It aims to streamline application development with customizable, pre-built components.'
}
],
maxPromptHistory: 30
};
public onSaveBtnClick(): void {
const promptHistory: PromptModel[] = this.editor.getAIPromptHistory();
console.log(promptHistory);
// Handle DB Post and save history to the DB.
}
}---
Custom Toolbar Configuration
Configure the order and items of toolbars in the header, prompt input, and response sections using headerToolbarSettings, promptToolbarSettings, and responseToolbarSettings.
Available Toolbar Items
Header Toolbar:
AIcommands– Opens AI-related command optionsClose– Closes the AI AssistantClear– Clears conversation history
Prompt Toolbar:
Edit– Modify prompt textCopy– Copy prompt to clipboard
Response Toolbar:
Regenerate– Generate new response for same promptCopy– Copy response to clipboard|– Visual separatorInsert– Insert response into editor
Default Toolbar Configurations
// Header: ['AIcommands', 'Close']
// Prompt: ['Edit', 'Copy']
// Response: ['Regenerate', 'Copy', '|', 'Insert']Example: Modified Toolbar Configuration
import { Component } from '@angular/core';
import { RichTextEditorModule, ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService, AIAssistantSettingsModel, ToolbarSettingsModel } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
templateUrl: 'app.component.html',
selector: 'app-root',
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService],
})
export class AppComponent {
public toolbarSettings: ToolbarSettingsModel = { items: ['AICommands', 'AIQuery'] };
public aiAssistantSettings: AIAssistantSettingsModel = {
headerToolbarSettings: ['AIcommands', 'Clear', 'Close'],
promptToolbarSettings: ['Edit'],
responseToolbarSettings: ['Copy', '|', 'Insert'],
prompts: [
{
prompt: 'What is Essential Studio ?',
response: 'Essential Studio is a software toolkit by Syncfusion that offers a variety of UI controls, frameworks, and libraries for developing applications on web, desktop, and mobile platforms. It aims to streamline application development with customizable, pre-built components.'
}
]
};
}---
Custom Toolbar Items
Add custom buttons, templates, and interactive elements to the AI Assistant toolbars using the aiAssistantToolbarClick event and toolbar item properties.
Custom Toolbar Item Properties
| Property | Description |
|---|---|
command | Primary command executed when item is clicked |
subCommand | Secondary sub-command for the item |
iconCss | CSS class for the toolbar icon |
text | Display text for the toolbar item |
type | Type of toolbar item (default: Button) |
align | Alignment within toolbar (Left or Right) |
visible | Whether item is visible (default: true) |
disabled | Whether item is disabled (default: false) |
tooltip | Tooltip text on hover |
cssClass | Additional CSS classes for styling |
template | Custom template for rendering (string or function) |
tabIndex | Tab order for keyboard navigation (default: -1) |
Example: Custom Toolbar Buttons with Event Handling
<ejs-richtexteditor
[aiAssistantSettings]="aiAssistantSettings"
(aiAssistantToolbarClick)="onAIAssistantToolbarClick($event)"
(beforePopupOpen)="beforePopupOpen($event)"
(beforePopupClose)="beforePopupClose($event)">
</ejs-richtexteditor>import { Component } from '@angular/core';
import { RichTextEditorModule, ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService, AIAssistantSettingsModel, ToolbarSettingsModel, AIAssitantToolbarClickEventArgs, BeforePopupOpenCloseEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
import { DropDownButton } from '@syncfusion/ej2-splitbuttons';
@Component({
imports: [RichTextEditorModule],
standalone: true,
templateUrl: 'app.component.html',
selector: 'app-root',
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, AIAssistantService],
})
export class AppComponent {
public userDropDown!: DropDownButton;
public aiAssistantSettings: AIAssistantSettingsModel = {
promptToolbarSettings: ['Edit', 'Copy', { command: 'Prompt', subCommand: 'Search', iconCss: 'e-icons e-open-link', tooltip: 'Search in Google' }],
responseToolbarSettings: [{ command: 'Response', subCommand: 'Save', iconCss: 'e-icons e-save', tooltip: 'Save Response' }, 'Regenerate', 'Copy', '|', 'Insert'],
headerToolbarSettings: ['AIcommands',
{ command: 'Header', subCommand: 'Profile', template: '<button id="profileDropDown" class="e-rte-dropdown-menu"></button>', align: 'Right' }, 'Close',],
prompts: [
{
prompt: 'What is Essential Studio ?',
response:
'Essential Studio is a software toolkit by Syncfusion that offers a variety of UI controls, frameworks, and libraries for developing applications on web, desktop, and mobile platforms. It aims to streamline application development with customizable, pre-built components.',
},
],
};
onAIAssistantToolbarClick(args: AIAssitantToolbarClickEventArgs) {
if (args.item.iconCss === 'e-icons e-open-link') {
const target: HTMLElement = args.originalEvent.target as HTMLElement;
const promptContainer: HTMLElement = target.closest('.e-prompt-container') as HTMLElement;
const prompt = (promptContainer.querySelector('.e-prompt-text') as HTMLElement).textContent;
window.open(`url${prompt}`, '_blank')
} else if (args.item.iconCss === 'e-icons e-save') {
const response = (args as any).event.currentTarget.closest('.e-output-container').querySelector('.e-content-body').innerHTML;
console.log(response); // Handle Saving response here.
}
};
beforePopupOpen(event: BeforePopupOpenCloseEventArgs) {
if (event.type === 'AIAssistant') {
this.userDropDown = new DropDownButton({
items: [
{ text: 'Usage', iconCss: 'e-icons e-chart' },
{ text: 'Saved Response' , iconCss: 'e-icons e-save'},
{ separator: true },
{ text: 'Log out', iconCss: 'e-icons e-view-side' }
],
iconCss: 'e-icons e-user',
cssClass: 'e-caret-hide',
}, event.element.querySelector('#profileDropDown') as HTMLButtonElement);
}
}
beforePopupClose(event: BeforePopupOpenCloseEventArgs) {
if (event.type === 'AIAssistant') {
this.userDropDown.destroy();
}
}
}---
Styling the AI Assistant Popup
Apply custom CSS to style the AI Assistant popup and its processing state.
Example: Custom Popup Styling
/* Base popup styling */
.e-rte-aiquery-popup {
padding: 2px;
}
/* Processing state styling */
.e-rte-aiquery-popup.processing {
padding: 2px;
color: white;
background: white;
z-index: 1;
}
/* Animated gradient border for processing state */
.e-rte-aiquery-popup.processing::before {
content: "";
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(270deg, #ff0080, #7928ca, #2afadf, #ff0080);
background-size: 600% 600%;
border-radius: 10px;
animation: gradient-animation 8s ease infinite;
}
.e-rte-aiquery-popup.processing::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: white;
border-radius: 6px;
z-index: -1;
}
@keyframes gradient-animation {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
Adding a Banner
Display welcome messages, instructions, or important information at the top of the AI Assistant popup using the bannerTemplate property.
Example: Custom Banner Template
public aiAssistantSettings: AIAssistantSettingsModel = {
bannerTemplate: `<div class="banner-content">
<div class="e-icons e-assistview-icon"></div>
<h3>AI Assistant</h3>
<i>AI-generated results can be error-prone; review them carefully.</i>
</div>`
};---
Handling AI Requests
Security Warning
The Rich Text Editor does not handle or send requests to AI provider endpoints. All AI interactions must be processed at the application layer (preferably in the backend).
Ensure that:
- AI inputs are validated and moderated on the backend.
- API keys and secrets are never exposed in request bodies or client-side code.
- All requests to AI services are securely routed and managed through backend systems.
The aiAssistantPromptRequest event fires when the user selects an AI command or submits a custom prompt. This event provides the necessary data to forward the request to your AI service.
Event Properties:
args.prompt— The AI command text or user's custom promptargs.text— The currently selected text in the editor at the time of request
---
Streaming Responses
Provides a typewriter-like streaming effect as chunks arrive:
async onAIRequest(args: AIAssistantPromptRequestArgs): Promise<void> {
try {
const response: Response = await fetch('/api/ai/stream', {
method: 'POST',
headers: {
"Content-Type": 'application/json',
"Authorization": 'HANDLE_AUTH_HERE'
},
body: JSON.stringify({ message: args.prompt + args.text }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error);
}
const stream: ReadableStream<string> = response.body.pipeThrough(new TextDecoderStream());
let fullText: string = '';
for await (const chunk of stream as unknown as AsyncIterable<string>) {
fullText += chunk;
// Updates the UI with each chunk for a streaming effect
this.editor.addAIPromptResponse(fullText, false);
}
// Final update to signal the end of the stream
this.editor.addAIPromptResponse(fullText, true);
} catch (error) {
console.error('AI request failed:', error);
}
}Note:addAIPromptResponse(content, finalUpdate)converts the provided Markdown response into HTML using the@syncfusion/ej2-markdown-converterpackage.
---
Non-Streaming (Single) Response
A complete response can be inserted at once by setting the finalUpdate parameter to true. While the response is being processed, a loading skeleton is displayed in the AssistView.
async onAIRequest(args: AIAssistantPromptRequestArgs): Promise<void> {
const response: Response = await fetch('/api/ai/stream', {
method: 'POST',
headers: {
"Content-Type": 'application/json',
"Authorization": 'HANDLE_AUTH_HERE'
},
body: JSON.stringify({ message: args.prompt + args.text }),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data: string = await response.text();
this.editor.addAIPromptResponse(data, true);
}---
Stop Responding Event
Fires when the user clicks the "Stop Responding" button. Use the aiAssistantStopRespondingClick event to cancel active fetch/streaming operations:
<ejs-richtexteditor
(aiAssistantPromptRequest)="onAIRequest($event)"
(aiAssistantStopRespondingClick)="onStopResponding($event)">
</ejs-richtexteditor>@Component({
selector: 'app-root',
template: `
<ejs-richtexteditor
(aiAssistantPromptRequest)="onAIRequest($event)"
(aiAssistantStopRespondingClick)="onStopResponding($event)">
</ejs-richtexteditor>
`
})
export class AppComponent {
private abortController: AbortController | null = null;
async onAIRequest(args: AIAssistantPromptRequestArgs): Promise<void> {
this.abortController = new AbortController();
try {
const response = await fetch('/api/stream', {
method: 'POST',
signal: this.abortController.signal,
body: JSON.stringify({ message: args.prompt + args.text })
});
// ... handle streaming
} catch (err: Error) {
if (err.name !== 'AbortError') throw err;
}
}
onStopResponding(args: AIAssistantStopRespondingArgs): void {
this.abortController?.abort();
}
}---
AI Toolbar Items Reference
| Toolbar item | Description |
|---|---|
AICommands | Opens predefined command menu: Improve, Shorten, Elaborate, Simplify, Summarize, Grammar Check |
AIQuery | Opens popup for custom prompt input. Keyboard shortcut: Alt+Enter (Win) / ⌥+Enter (Mac) |
Clipboard and Paste Cleanup in Syncfusion Angular Rich Text Editor
Table of Contents
- Overview
- Paste Cleanup Configuration
- Paste Modes
- Filtering Tags and Attributes
- Clipboard Cleanup (Copy/Cut)
- afterPasteCleanup Event
---
Overview
The RTE provides two services for controlling clipboard behavior:
| Service | When to use |
|---|---|
PasteCleanupService | Control what happens when content is pasted into the editor |
ClipboardCleanupService | Clean up content when it is copied or cut from the editor |
---
Paste Cleanup Configuration
Requires PasteCleanupService in providers.
import { Component } from '@angular/core';
import {
RichTextEditorModule, PasteCleanupSettingsModel,
ToolbarService, HtmlEditorService, LinkService,
ImageService, QuickToolbarService, PasteCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[pasteCleanupSettings]="pasteSettings">
</ejs-richtexteditor>
`,
providers: [ToolbarService, HtmlEditorService, LinkService,
ImageService, QuickToolbarService, PasteCleanupService]
})
export class AppComponent {
public pasteSettings: PasteCleanupSettingsModel = {
keepFormat: true, // Preserve source formatting (default: true)
deniedTags: ['script', 'style', 'iframe'], // Remove these tags
deniedAttrs: ['class', 'id'], // Remove these attributes
allowedStyleProps: ['color', 'font-size', 'font-weight'] // Only keep these CSS properties
};
}---
Paste Modes
The three modes are mutually exclusive — set only one to true at a time:
Mode 1: Prompt Dialog (let user choose)
Opens a dialog with three options: Keep (preserve formatting), Clean (remove styles), Plain Text.
public pasteSettings: PasteCleanupSettingsModel = {
prompt: true // Show paste options dialog
};Whenprompt: true, theplainTextandkeepFormatsettings are ignored.
Mode 2: Always Plain Text
Strips all HTML tags and styles — pastes only raw text:
public pasteSettings: PasteCleanupSettingsModel = {
plainText: true,
prompt: false,
keepFormat: false
};Mode 3: Keep Formatting (with filters)
Preserves original formatting but applies deniedTags, deniedAttrs, and allowedStyleProps filters:
public pasteSettings: PasteCleanupSettingsModel = {
keepFormat: true,
prompt: false,
plainText: false,
allowedStyleProps: ['font-size', 'color', 'font-weight', 'text-decoration']
};Mode 4: Clean Format (default fallback)
When all three (prompt, plainText, keepFormat) are false, inline styles are removed but structural tags (<p>, <ul>, <table>) are kept:
public pasteSettings: PasteCleanupSettingsModel = {
prompt: false,
plainText: false,
keepFormat: false
};---
Filtering Tags and Attributes
deniedTags — remove specific HTML elements:
public pasteSettings: PasteCleanupSettingsModel = {
keepFormat: true,
deniedTags: [
'script', // Remove all <script> tags
'iframe', // Remove all <iframe> tags
'style', // Remove all <style> tags
'a[!href]', // Remove <a> tags that have NO href attribute
'a[href, target]' // Remove <a> tags that have BOTH href AND target
]
};deniedAttrs — remove specific attributes from all tags:
public pasteSettings: PasteCleanupSettingsModel = {
keepFormat: true,
deniedAttrs: ['class', 'id', 'style', 'data-*']
};allowedStyleProps — whitelist specific CSS properties (only applies when keepFormat is true):
public pasteSettings: PasteCleanupSettingsModel = {
keepFormat: true,
allowedStyleProps: [
'color', 'background-color', 'font-size', 'font-family',
'font-weight', 'font-style', 'text-decoration', 'text-align'
]
};---
Clipboard Cleanup (Copy/Cut)
ClipboardCleanupService automatically cleans content when users copy or cut from the editor — removing unwanted inline styles while preserving structure.
import {
RichTextEditorModule, ToolbarService, HtmlEditorService,
ClipboardCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
providers: [
ToolbarService, HtmlEditorService,
ClipboardCleanupService // Adds cleanup on copy/cut
]
})
export class AppComponent {}No additional configuration needed — injecting the service enables it.
---
afterPasteCleanup Event
Fires after paste cleanup is complete. Use it to inspect or further transform the pasted content:
import { AfterPasteCleanupEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `
<ejs-richtexteditor
(afterPasteCleanup)="onAfterPaste($event)">
</ejs-richtexteditor>
`
})
export class AppComponent {
onAfterPaste(args: AfterPasteCleanupEventArgs): void {
// args.value — the cleaned HTML string about to be inserted
console.log('Pasted content after cleanup:', args.value);
// Optionally modify args.value before insertion
// args.value = args.value.replace(/some-pattern/g, 'replacement');
}
}---
Quick Reference: pasteCleanupSettings
| Property | Default | Description |
|---|---|---|
prompt | false | Show dialog letting user choose paste mode |
plainText | false | Strip all tags, paste as plain text |
keepFormat | true | Preserve source formatting with optional filters |
deniedTags | null | HTML tags to remove from pasted content |
deniedAttrs | null | HTML attributes to strip from all pasted elements |
allowedStyleProps | [] | CSS properties to keep (only applies when keepFormat: true) |
Editor Types in Syncfusion Angular Rich Text Editor
Table of Contents
- Choosing the Right Editor Type
- Default DIV Editor (HTML Mode)
- Markdown Editor Mode
- IFrame Editor
- Inline Editing
- Resizable Editor
- Decision Guide
---
Choosing the Right Editor Type
| Use case | Editor type |
|---|---|
| Standard content editing, forms | Default DIV (HTML mode) |
| Markdown content (blogs, wikis) | Markdown mode |
| Isolated CSS, no style leakage | IFrame mode |
| Edit content in-place on a page | Inline editing |
| User-resizable editor area | Resizable editor |
---
Default DIV Editor (HTML Mode)
The default editor renders inside a <div> element. This is the standard use case — output is valid HTML.
The editorMode defaults to 'HTML'. To explicitly set it:
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarService, HtmlEditorService,
QuickToolbarService, ImageService, LinkService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor [editorMode]="'HTML'" [(value)]="content"></ejs-richtexteditor>`,
providers: [ToolbarService, HtmlEditorService, QuickToolbarService, ImageService, LinkService]
})
export class AppComponent {
public content: string = '<p>HTML editor content</p>';
}Required service: HtmlEditorService---
Markdown Editor Mode
Sets editorMode to 'Markdown'. The editor outputs Markdown text. Use a library like marked or showdown to convert to HTML for preview.
Supported Markdown tags: h1–h6, blockquote, pre, p, ol, ul Supported inline: Bold, Italic, StrikeThrough, InlineCode, SubScript, SuperScript, UpperCase, LowerCase Supported inserts: Image, Link, Table
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarService, LinkService,
ImageService, MarkdownEditorService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[editorMode]="mode"
[(value)]="markdownContent">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService]
})
export class AppComponent {
public mode: string = 'Markdown';
public markdownContent: string = '# Hello\n\nThis is **Markdown** content.';
}Required service:MarkdownEditorService(instead ofHtmlEditorService)
---
IFrame Editor
Renders the editor inside an <iframe>, fully isolating it from the host page's CSS and JavaScript. Use this when:
- Parent page styles conflict with editor content styling
- You need to inject custom CSS/scripts into the editor area
- You want full CSS encapsulation
import { Component } from '@angular/core';
import {
RichTextEditorModule, IFrameSettingsModel,
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor [iframeSettings]="iframe" [(value)]="content"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public content: string = '<p>IFrame editor content</p>';
public iframe: IFrameSettingsModel = { enable: true };
}Injecting custom CSS and scripts into the iframe:
public iframe: IFrameSettingsModel = {
enable: true,
resources: {
scripts: ['/assets/editor-scripts.js'],
styles: ['/assets/editor-content.css']
}
};Applying custom attributes to the iframe body:
public iframe: IFrameSettingsModel = {
enable: true,
attributes: { 'data-theme': 'dark' }
};Gotcha: When using Mention inside an IFrame editor, set mentionObj.target = rteObj.inputElement after the RTE is created. See smart-editing.md for the full example.---
Inline Editing
Renders the editor without a visible toolbar by default. The toolbar appears as a floating popup when the user selects text (or focuses the area). Ideal for in-place editing inside a page.
import { Component } from '@angular/core';
import {
RichTextEditorModule, InlineModeModel, ToolbarSettingsModel,
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[inlineMode]="inlineMode"
[toolbarSettings]="toolbarSettings"
[(value)]="content">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public content: string = '<p>Click to select text and see the toolbar appear.</p>';
// onSelection: true → toolbar appears only when text is selected
// onSelection: false → toolbar appears when the editor is focused
public inlineMode: InlineModeModel = { enable: true, onSelection: true };
public toolbarSettings: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'Underline', 'Formats', 'Alignments', 'CreateLink']
};
}---
Resizable Editor
Adds a drag handle at the bottom-right corner that lets users resize the editor area.
Required service: ResizeServiceimport { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarService, LinkService,
ImageService, ResizeService, HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor [enableResize]="true" [(value)]="content"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, ResizeService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public content: string = '<p>Drag the bottom-right handle to resize.</p>';
}Constraining resize bounds (app.component.css):
.e-richtexteditor {
min-width: 250px;
max-width: 880px;
min-height: 200px;
max-height: 600px;
}---
Decision Guide
User needs to select content to format it → Inline editing
public inlineMode: InlineModeModel = { enable: true, onSelection: true };App uses Tailwind/global styles that break editor content → IFrame mode
public iframe: IFrameSettingsModel = { enable: true };Output should be Markdown not HTML → Markdown mode
public mode: EditorMode = 'Markdown';
// inject MarkdownEditorService instead of HtmlEditorServiceUser should be able to resize the editor → Resizable + ResizeService
[enableResize]="true"
// providers: [..., ResizeService]Editor Value, Selection & Commands in Syncfusion Angular Rich Text Editor
Table of Contents
- Getting and Setting Value
- Placeholder Text
- executeCommand API
- Undo/Redo Configuration
- Selection and Cursor Control
---
Getting and Setting Value
Setting content via property binding:
// Two-way binding (recommended)
<ejs-richtexteditor [(value)]="htmlContent"></ejs-richtexteditor>
// One-way input
<ejs-richtexteditor [value]="htmlContent"></ejs-richtexteditor>Setting content via ng-template (valueTemplate):
@Component({
template: `
<ejs-richtexteditor>
<ng-template #valueTemplate>
<p>This is initial content set via <strong>template</strong>.</p>
</ng-template>
</ejs-richtexteditor>
`
})Getting value programmatically:
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorComponent } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `
<ejs-richtexteditor #rte [(value)]="content"></ejs-richtexteditor>
<button (click)="saveContent()">Save</button>
`
})
export class AppComponent {
@ViewChild('rte') rteObj!: RichTextEditorComponent;
public content: string = '';
saveContent(): void {
const html = this.rteObj.value; // Get current HTML value
console.log('Content:', html);
}
}Listening to change events:
import { ChangeEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
// change fires when editor loses focus AND content has changed
(change)="onChange($event)"
// input fires on every keystroke
(input)="onInput($event)"
onChange(args: ChangeEventArgs): void {
console.log(args.value); // Updated HTML string
}---
Placeholder Text
// Via property
<ejs-richtexteditor placeholder="Start typing here..."></ejs-richtexteditor>Style the placeholder via CSS:
.e-richtexteditor .e-rte-placeholder {
font-family: monospace;
color: #aaa;
}---
executeCommand API
executeCommand programmatically applies formatting or inserts content at the current cursor position. Only works in HTML mode (not Markdown).
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorComponent } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor #rte></ejs-richtexteditor>`
})
export class AppComponent {
@ViewChild('rte') rteObj!: RichTextEditorComponent;
applyFormatting(): void {
this.rteObj.executeCommand('bold');
this.rteObj.executeCommand('italic');
this.rteObj.executeCommand('fontColor', '#FF0000');
this.rteObj.executeCommand('fontSize', '18pt');
this.rteObj.executeCommand('fontName', 'Arial');
this.rteObj.executeCommand('formatBlock', 'H2');
}
insertContent(): void {
this.rteObj.executeCommand('insertText', 'Inserted text here');
this.rteObj.executeCommand('insertHTML', '<b>Bold HTML</b>');
this.rteObj.executeCommand('insertImage', {
url: 'url',
cssClass: 'rte-img'
});
this.rteObj.executeCommand('createLink', {
text: 'Syncfusion',
url: 'https://syncfusion.com',
title: 'Syncfusion'
});
}
}Full executeCommand reference:
| Command | Description | Example |
|---|---|---|
bold | Bold selected text | executeCommand('bold') |
italic | Italic selected text | executeCommand('italic') |
underline | Underline selected text | executeCommand('underline') |
strikeThrough | Strikethrough | executeCommand('strikeThrough') |
superscript | Superscript | executeCommand('superscript') |
subscript | Subscript | executeCommand('subscript') |
uppercase | To uppercase | executeCommand('uppercase') |
lowercase | To lowercase | executeCommand('lowercase') |
fontColor | Set font color | executeCommand('fontColor', '#red') |
fontName | Set font family | executeCommand('fontName', 'Arial') |
fontSize | Set font size | executeCommand('fontSize', '14pt') |
formatBlock | Set block format | executeCommand('formatBlock', 'H1') |
backColor | Set background color | executeCommand('backColor', 'yellow') |
justifyCenter | Center align | executeCommand('justifyCenter') |
justifyLeft | Left align | executeCommand('justifyLeft') |
justifyRight | Right align | executeCommand('justifyRight') |
justifyFull | Justify | executeCommand('justifyFull') |
indent | Increase indent | executeCommand('indent') |
outdent | Decrease indent | executeCommand('outdent') |
insertOrderedList | Numbered list | executeCommand('insertOrderedList') |
insertUnorderedList | Bulleted list | executeCommand('insertUnorderedList') |
insertHTML | Insert HTML at cursor | executeCommand('insertHTML', '<b>text</b>') |
insertText | Insert plain text | executeCommand('insertText', 'hello') |
insertImage | Insert image | executeCommand('insertImage', { url, cssClass }) |
insertVideo | Insert video | executeCommand('insertVideo', { url, cssClass }) |
insertAudio | Insert audio | executeCommand('insertAudio', { url, cssClass }) |
createLink | Insert link | executeCommand('createLink', { text, url, title }) |
removeFormat | Remove all formatting | executeCommand('removeFormat') |
undo | Undo | executeCommand('undo') |
redo | Redo | executeCommand('redo') |
---
Undo/Redo Configuration
@Component({
template: `
<ejs-richtexteditor
[undoRedoSteps]="50"
[undoRedoTimer]="400"
[toolbarSettings]="{ items: ['Undo', 'Redo'] }">
</ejs-richtexteditor>
`
})| Property | Default | Description |
|---|---|---|
undoRedoSteps | 30 | Max undo/redo history entries |
undoRedoTimer | 300 | Debounce time (ms) between history snapshots |
Clear the undo/redo stack (e.g., when loading new content):
@ViewChild('rte') rteObj!: RichTextEditorComponent;
loadNewContent(html: string): void {
this.rteObj.value = html;
this.rteObj.clearUndoRedo(); // Clear history so user can't undo to old content
}---
Selection and Cursor Control
Position cursor at end of content (on created event):
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorComponent, NodeSelection } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor #rte (created)="onCreated()" [(value)]="content"></ejs-richtexteditor>`
})
export class AppComponent {
@ViewChild('rte') rteObj!: RichTextEditorComponent;
public content: string = '<p>Some content</p>';
onCreated(): void {
// Focus and move cursor to end
this.rteObj.focusIn();
const editPanel = this.rteObj.contentModule.getEditPanel() as HTMLElement;
const range = document.createRange();
range.selectNodeContents(editPanel);
range.collapse(false); // false = collapse to end
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
}
}Get current selection range:
import { NodeSelection } from '@syncfusion/ej2-angular-richtexteditor';
const nodeSelection = new NodeSelection();
const range = nodeSelection.getRange(document);
// Save range for later use (e.g., before opening a dialog)
this.savedRange = range;Restore a saved range (re-apply selection):
nodeSelection.setRange(document, this.savedRange);Pattern: Save the range before opening a custom dialog, then restore it when inserting content — this ensures insertHTML lands at the user's original cursor position.Rich Text Editor Events — Core
Table of Contents
- Lifecycle Events
- Action Events
- Content Change Events
- Dialog Events
- Popup Events
- Quick Toolbar Events
- Resize Events
---
Lifecycle Events
created
Triggers when the Rich Text Editor is rendered.
Event Type: EmitType<Object>
Example:
import { Component } from '@angular/core';
@Component({
template: `<ejs-richtexteditor (created)="onCreated()"></ejs-richtexteditor>`
})
export class AppComponent {
onCreated(): void {
console.log('Editor has been created');
// Initialize custom configurations
// Load saved content
}
}destroyed
Triggers when the Rich Text Editor is destroyed.
Event Type: EmitType<Object>
Example:
onDestroyed(): void {
console.log('Editor has been destroyed');
// Cleanup operations
// Save state before destruction
}focus
Triggers when the Rich Text Editor gains focus.
Event Type: EmitType<Object>
Example:
onFocus(): void {
console.log('Editor focused');
this.showToolbarHints = true;
}blur
Triggers when the Rich Text Editor loses focus.
Event Type: EmitType<Object>
Example:
onBlur(): void {
console.log('Editor blurred');
this.autoSaveContent();
}---
Action Events
actionBegin
Triggers before executing a command via toolbar items. Cancel this event to prevent the command from executing by setting args.cancel = true.
Event Type: EmitType<ActionBeginEventArgs>
Event Arguments:
cancel(boolean): Set totrueto cancel the actionname(string): Name of the eventoriginalEvent(MouseEvent | KeyboardEvent | DragEvent): Original event that triggered the actionrequestType(string): Type of the current actionselectType(string): Selection type (dropdown or not)
Example:
import { ActionBeginEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (actionBegin)="onActionBegin($event)"></ejs-richtexteditor>`
})
export class AppComponent {
onActionBegin(args: ActionBeginEventArgs): void {
console.log('Action:', args.requestType);
// Prevent certain actions
if (args.requestType === 'Image' && !this.userCanUploadImages) {
args.cancel = true;
alert('You do not have permission to upload images');
}
// Log user actions for audit
this.logUserAction(args.requestType);
}
}actionComplete
Triggers after executing a command via toolbar items.
Event Type: EmitType<ActionCompleteEventArgs>
Event Arguments:
editorMode(string): Current mode of the editorevent(MouseEvent | KeyboardEvent): Event associated with the actionname(string): Name of the eventrequestType(string): Type of the completed action
Example:
import { ActionCompleteEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onActionComplete(args: ActionCompleteEventArgs): void {
console.log('Action completed:', args.requestType);
// Track formatting actions
if (args.requestType === 'Bold' || args.requestType === 'Italic') {
this.trackFormattingUsage(args.requestType);
}
// Update UI after specific actions
if (args.requestType === 'FullScreen') {
this.isFullScreen = true;
}
}---
Content Change Events
change
Triggers when the Rich Text Editor loses focus AND changes have been made to the content.
Event Type: EmitType<ChangeEventArgs>
Event Arguments:
isInteracted(boolean): Specifies if triggered by user interaction or auto-savename(string): Name of the eventvalue(string): Current value/content of the editor
Example:
import { ChangeEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (change)="onChange($event)"></ejs-richtexteditor>`
})
export class AppComponent {
onChange(args: ChangeEventArgs): void {
console.log('Content changed:', args.value);
console.log('Is user interaction:', args.isInteracted);
// Save to database
this.saveContent(args.value);
// Update character count
this.updateCharacterCount(args.value);
}
saveContent(content: string): void {
// Auto-save implementation
this.http.post('/api/save', { content }).subscribe(
() => console.log('Content saved'),
(error) => console.error('Save failed:', error)
);
}
}selectionChanged
Triggers when a non-empty text selection is made or updated in the editor. Fires in both HTML and Markdown modes.
Event Type: EmitType<SelectionChangedEventArgs>
Event Arguments:
editorMode(EditorMode | string): Current editor mode (HTML or Markdown)selectedContent(string): HTML string of the selected contentselection(Selection): Browser Selection object
Example:
import { SelectionChangedEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (selectionChanged)="onSelectionChanged($event)"></ejs-richtexteditor>`
})
export class AppComponent {
onSelectionChanged(args: SelectionChangedEventArgs): void {
console.log('Selected content:', args.selectedContent);
console.log('Editor mode:', args.editorMode);
// Show custom context menu for selection
if (args.selectedContent) {
this.showContextMenu(args.selection);
}
// Enable/disable custom buttons based on selection
this.updateCustomToolbar(args.selectedContent);
}
showContextMenu(selection: Selection): void {
console.log('Selection range:', selection.rangeCount);
}
}---
Dialog Events
beforeDialogOpen
Triggers before a dialog is opened. Cancel by setting args.cancel = true.
Event Type: EmitType<BeforeOpenEventArgs>
Example:
onBeforeDialogOpen(args: BeforeOpenEventArgs): void {
console.log('Dialog opening');
// Prevent dialog from opening under certain conditions
if (!this.userHasPermission) {
args.cancel = true;
alert('You do not have permission to perform this action');
}
}dialogOpen
Triggers when a dialog is opened.
Event Type: EmitType<Object>
Example:
onDialogOpen(): void {
console.log('Dialog opened');
// Custom initialization for dialog
}beforeDialogClose
Triggers before a dialog is closed. Cancel by setting args.cancel = true.
Event Type: EmitType<BeforeCloseEventArgs>
Example:
onBeforeDialogClose(args: BeforeCloseEventArgs): void {
// Validate form before closing
if (this.hasUnsavedChanges) {
const confirmClose = confirm('You have unsaved changes. Close anyway?');
args.cancel = !confirmClose;
}
}dialogClose
Triggers after a dialog has been closed.
Event Type: EmitType<Object>
Example:
onDialogClose(): void {
console.log('Dialog closed');
// Cleanup or refresh operations
}---
Popup Events
beforePopupOpen
Triggers before a popup is about to open. Can be canceled by setting args.cancel = true.
Event Type: EmitType<BeforePopupOpenCloseEventArgs>
Event Arguments:
cancel(boolean): Set totrueto prevent popup from openingelement(HTMLElement): Popup HTML elementoriginalEvent(Event): Original DOM eventpopupInstance(any): Popup instancetype(EditorPopupType): Type of popup (EmojiPicker, AIAssistant)
Example:
import { BeforePopupOpenCloseEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onBeforePopupOpen(args: BeforePopupOpenCloseEventArgs): void {
console.log('Opening popup:', args.type);
// Add custom elements to popup
if (args.type === 'EmojiPicker') {
this.customizeEmojiPicker(args.element);
}
// Prevent popup under certain conditions
if (!this.featureEnabled) {
args.cancel = true;
}
}beforePopupClose
Triggers before a popup is about to close. Can be canceled by setting args.cancel = true.
Event Type: EmitType<BeforePopupOpenCloseEventArgs>
Example:
onBeforePopupClose(args: BeforePopupOpenCloseEventArgs): void {
console.log('Closing popup:', args.type);
// Cleanup custom elements
if (args.type === 'AIAssistant') {
this.cleanupAIAssistant(args.element);
}
}---
Quick Toolbar Events
beforeQuickToolbarOpen
Triggers before the quick toolbar opens.
Event Type: EmitType<BeforeQuickToolbarOpenArgs>
Event Arguments:
cancel(boolean): Set totrueto prevent quick toolbar from openingtargetElement(Element): Target element that triggered the toolbar
Example:
import { BeforeQuickToolbarOpenArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (beforeQuickToolbarOpen)="onBeforeQuickToolbar($event)"></ejs-richtexteditor>`,
providers: [QuickToolbarService]
})
export class AppComponent {
onBeforeQuickToolbar(args: BeforeQuickToolbarOpenArgs): void {
console.log('Quick toolbar opening for:', args.targetElement);
// Prevent quick toolbar for specific elements
if (args.targetElement.tagName === 'TABLE' && !this.allowTableEditing) {
args.cancel = true;
}
}
}quickToolbarOpen
Triggers when the quick toolbar is opened.
Event Type: EmitType<Object>
Example:
onQuickToolbarOpen(): void {
console.log('Quick toolbar opened');
}quickToolbarClose
Triggers after the quick toolbar has been closed.
Event Type: EmitType<Object>
Example:
onQuickToolbarClose(): void {
console.log('Quick toolbar closed');
}---
Resize Events
resizeStart
Triggers when resizing starts for tables, images, videos, or the editor itself.
Event Type: EmitType<ResizeArgs>
Event Arguments:
cancel(boolean): Set totrueto prevent resizeevent(MouseEvent | TouchEvent): Resize eventrequestType(string): Type of request
Example:
import { ResizeArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor [enableResize]="true" (resizeStart)="onResizeStart($event)"></ejs-richtexteditor>`,
providers: [ResizeService]
})
export class AppComponent {
onResizeStart(args: ResizeArgs): void {
console.log('Resize started:', args.requestType);
// Prevent resize for certain elements
if (this.isReadOnly) {
args.cancel = true;
}
}
}resizing
Triggers continuously while resizing elements.
Event Type: EmitType<ResizeArgs>
Example:
onResizing(args: ResizeArgs): void {
console.log('Resizing in progress');
// Show current dimensions
const target = args.event.target as HTMLElement;
console.log('Current size:', target.offsetWidth, target.offsetHeight);
}resizeStop
Triggers when resizing stops.
Event Type: EmitType<ResizeArgs>
Example:
onResizeStop(args: ResizeArgs): void {
console.log('Resize stopped:', args.requestType);
// Save new dimensions
this.saveResizeState();
}---
Common Event Patterns
Comprehensive Event Logging
@Component({
template: `
<ejs-richtexteditor
(created)="onCreated()"
(actionBegin)="onActionBegin($event)"
(actionComplete)="onActionComplete($event)"
(change)="onChange($event)"
(selectionChanged)="onSelectionChanged($event)"
(toolbarClick)="onToolbarClick($event)"
(imageUploadSuccess)="onImageUploadSuccess($event)"
(imageUploadFailed)="onImageUploadFailed($event)">
</ejs-richtexteditor>
`
})
export class AppComponent {
eventLog: string[] = [];
logEvent(eventName: string, details?: any): void {
const timestamp = new Date().toISOString();
this.eventLog.push(`[${timestamp}] ${eventName}: ${JSON.stringify(details)}`);
}
onCreated(): void {
this.logEvent('created');
}
onActionBegin(args: ActionBeginEventArgs): void {
this.logEvent('actionBegin', { requestType: args.requestType });
}
// ... other event handlers
}Form Validation with Events
@Component({
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<ejs-richtexteditor
formControlName="content"
(change)="onContentChange($event)"
(blur)="onBlur()">
</ejs-richtexteditor>
<button type="submit">Submit</button>
</form>
`
})
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
content: ['', [Validators.required, Validators.minLength(50)]]
});
}
onContentChange(args: ChangeEventArgs): void {
// Update form control
this.form.patchValue({ content: args.value });
// Validate on change
if (this.form.get('content')?.invalid) {
console.log('Content validation failed');
}
}
onBlur(): void {
// Mark as touched on blur
this.form.get('content')?.markAsTouched();
}
onSubmit(): void {
if (this.form.valid) {
console.log('Form submitted:', this.form.value);
}
}
}---
See Also
- properties.md - Component properties and configuration
- methods.md - Programmatic API methods
- toolbar.md - Toolbar event handling
- ai-assistant.md - AI-specific events
Rich Text Editor Events — Media, Upload & Toolbar
Table of Contents
- AI Assistant Events
- Media Events
- Paste and Clipboard Events
- Toolbar Events
- Upload Events
- Document Import/Export Events
---
AI Assistant Events
aiAssistantPromptRequest
Triggers when a user sends a prompt to the AI Assistant. Use this event to handle the request with your preferred AI Provider.
Event Type: EmitType<AIAssistantPromptRequestArgs>
Event Arguments:
cancel(boolean): Set totrueto cancel the prompt requesthtml(string): Current selection as HTMLtext(string): Current selection as plain textprompt(string): The prompt textpromptSuggestions(string[]): List of prompt suggestionsresponseToolbarItems(ToolbarItemModel[]): Toolbar items for the output view
Example:
import { AIAssistantPromptRequestArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (aiAssistantPromptRequest)="onAIPromptRequest($event)"></ejs-richtexteditor>`,
providers: [AIAssistantService]
})
export class AppComponent {
@ViewChild('rte') rteObj!: RichTextEditorComponent;
async onAIPromptRequest(args: AIAssistantPromptRequestArgs): Promise<void> {
console.log('AI Prompt:', args.prompt);
console.log('Selected text:', args.text);
console.log('Selected HTML:', args.html);
try {
const response = await this.callOpenAI(args.prompt, args.text);
this.rteObj.addAIPromptResponse(response, true);
} catch (error) {
console.error('AI request failed:', error);
this.rteObj.addAIPromptResponse('Error: Unable to generate response', true);
}
}
async callOpenAI(prompt: string, context: string): Promise<string> {
const response = await fetch('url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'system', content: 'You are a writing assistant.' },
{ role: 'user', content: `${prompt}\n\nContext: ${context}` }
]
})
});
const data = await response.json();
return data.choices[0].message.content;
}
}aiAssistantStopRespondingClick
Triggers when the user clicks the stop responding button in the AI Assistant. Helps cancel ongoing AI query requests.
Event Type: EmitType<AIAssistantStopRespondingArgs>
Event Arguments:
dataIndex(number): Index of the prompt in the listevent(Event): Event object associated with the actionprompt(string): Prompt text associated with the request
Example:
import { AIAssistantStopRespondingArgs } from '@syncfusion/ej2-angular-richtexteditor';
onAIStopResponding(args: AIAssistantStopRespondingArgs): void {
console.log('Stop responding clicked for:', args.prompt);
// Cancel ongoing API request
if (this.currentAIRequest) {
this.currentAIRequest.abort();
}
}aiAssistantToolbarClick
Triggers when a user selects an item from the AI Assistant toolbar. Use this event to handle custom toolbar item clicks.
Event Type: EmitType<AIAssitantToolbarClickEventArgs>
Event Arguments:
cancel(boolean): Set totrueto cancel the default actiondataIndex(number): Index of the message data (not applicable for header toolbar)item(IAIAssistantToolbarItem): Toolbar item that was clickedoriginalEvent(Event): Original event objectrequestType(AssistantToolbarType): Toolbar section where click occurred (Header, Prompt, Response, Footer)
Example:
import { AIAssitantToolbarClickEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onAIToolbarClick(args: AIAssitantToolbarClickEventArgs): void {
console.log('AI Toolbar item:', args.item.text);
console.log('Request type:', args.requestType);
// Handle custom toolbar actions
if (args.item.command === 'CustomExport') {
this.exportAIConversation();
args.cancel = true; // Prevent default action
}
}---
Media Events
afterImageDelete
Triggers when a selected image is removed from the editor content.
Event Type: EmitType<AfterImageDeleteEventArgs>
Event Arguments:
element(Node): Image DOM element that was deletedsrc(string): 'src' attribute of the deleted image
Example:
import { AfterImageDeleteEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onAfterImageDelete(args: AfterImageDeleteEventArgs): void {
console.log('Image deleted:', args.src);
// Remove image from server
this.http.delete(`/api/images?src=${encodeURIComponent(args.src)}`)
.subscribe(
() => console.log('Image removed from server'),
(error) => console.error('Failed to delete image:', error)
);
}afterMediaDelete
Triggers when selected media (audio/video) is removed from the editor content.
Event Type: EmitType<AfterMediaDeleteEventArgs>
Event Arguments:
element(Node): Audio/video DOM element that was deletedsrc(string): 'src' attribute of the deleted media
Example:
import { AfterMediaDeleteEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onAfterMediaDelete(args: AfterMediaDeleteEventArgs): void {
console.log('Media deleted:', args.src);
// Clean up server storage
this.deleteMediaFromServer(args.src);
}beforeImageDrop
Triggers before an image is dropped into the editor.
Event Type: EmitType<ImageDropEventArgs>
Event Arguments:
cancel(boolean): Set totrueto prevent the dropdataTransfer(DataTransfer | null): DataTransfer object for the eventrangeOffset(number): Offset value for the drop rangerangeParent(Element): Parent element of the drop range- Plus standard drag event properties
Example:
import { ImageDropEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onBeforeImageDrop(args: ImageDropEventArgs): void {
const files = args.dataTransfer?.files;
if (files && files.length > 0) {
const file = files[0];
// Check file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
args.cancel = true;
alert('Image size must be less than 5MB');
}
// Check file type
if (!file.type.startsWith('image/')) {
args.cancel = true;
alert('Only image files are allowed');
}
}
}beforeMediaDrop
Triggers before media (audio/video) is dropped into the editor.
Event Type: EmitType<MediaDropEventArgs>
Event Arguments:
- Similar to
beforeImageDropwith additionalmediaTypeproperty
Example:
import { MediaDropEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onBeforeMediaDrop(args: MediaDropEventArgs): void {
console.log('Media type:', args.mediaType);
console.log('Drop position:', args.rangeOffset);
if (args.mediaType === 'video' && !this.allowVideoUpload) {
args.cancel = true;
alert('Video uploads are not allowed');
}
}---
Paste and Clipboard Events
beforePasteCleanup
Triggers before cleaning up copied content.
Event Type: EmitType<PasteCleanupArgs>
Event Arguments:
filesData(FileInfo[]): List of image file data pastedvalue(string): Content data from ClipboardEvent
Example:
import { PasteCleanupArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (beforePasteCleanup)="onBeforePaste($event)"></ejs-richtexteditor>`,
providers: [PasteCleanupService]
})
export class AppComponent {
onBeforePaste(args: PasteCleanupArgs): void {
console.log('Pasting content:', args.value);
if (args.filesData.length > 0) {
console.log(`Pasting ${args.filesData.length} images`);
}
if (args.value.includes('<script>')) {
console.warn('Potentially harmful content detected');
}
}
}afterPasteCleanup
Triggers after cleaning up copied content.
Event Type: EmitType<object>
Example:
onAfterPasteCleanup(): void {
console.log('Paste cleanup completed');
// Additional processing after paste
}beforeClipboardWrite
Triggers before setting copy or cut clipboard data.
Event Type: EmitType<ClipboardWriteEventArgs>
Example:
onBeforeClipboardWrite(args: ClipboardWriteEventArgs): void {
console.log('Content being copied/cut');
this.logClipboardAction('copy');
}---
Toolbar Events
toolbarClick
Triggers when a toolbar item is clicked.
Event Type: EmitType<Object>
Example:
@Component({
template: `<ejs-richtexteditor (toolbarClick)="onToolbarClick($event)"></ejs-richtexteditor>`,
providers: [ToolbarService]
})
export class AppComponent {
onToolbarClick(args: any): void {
console.log('Toolbar item clicked:', args.item);
// Custom handling for specific toolbar items
if (args.item.subCommand === 'CustomButton') {
this.handleCustomAction();
}
}
}updatedToolbarStatus
Triggers when the toolbar items status is updated (particularly undo/redo).
Event Type: EmitType<ToolbarStatusEventArgs>
Event Arguments:
html(object): HTML toolbar statusmarkdown(object): Markdown toolbar statusname(string): Event nameredo(boolean): Indicates if redo action can be performedundo(boolean): Indicates if undo action can be performed
Example:
import { ToolbarStatusEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onToolbarStatusUpdate(args: ToolbarStatusEventArgs): void {
console.log('Undo available:', args.undo);
console.log('Redo available:', args.redo);
// Update custom undo/redo buttons
this.canUndo = args.undo;
this.canRedo = args.redo;
}slashMenuItemSelect
Triggers when a slash menu item is selected.
Event Type: EmitType<SlashMenuItemSelectArgs>
Event Arguments:
cancel(boolean): Set totrueto cancel default actionisInteracted(boolean): If triggered by user interactionitem(HTMLLIElement): Selected list itemitemData(ISlashMenuItem): Slash menu item dataoriginalEvent(MouseEvent | KeyboardEvent | TouchEvent): Original event
Example:
import { SlashMenuItemSelectArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (slashMenuItemSelect)="onSlashMenuSelect($event)"></ejs-richtexteditor>`,
providers: [SlashMenuService]
})
export class AppComponent {
onSlashMenuSelect(args: SlashMenuItemSelectArgs): void {
console.log('Slash menu item:', args.itemData.text);
console.log('Command:', args.itemData.command);
// Handle custom slash commands
if (args.itemData.command === 'custom-template') {
this.insertCustomTemplate();
args.cancel = true; // Prevent default action
}
}
}---
Upload Events
beforeImageUpload
Triggers before the image upload process starts.
Event Type: EmitType<BeforeUploadEventArgs>
Example:
@Component({
template: `<ejs-richtexteditor (beforeImageUpload)="onBeforeImageUpload($event)"></ejs-richtexteditor>`,
providers: [ImageService]
})
export class AppComponent {
onBeforeImageUpload(args: BeforeUploadEventArgs): void {
// Add custom headers to upload request
args.currentRequest = args.currentRequest || [];
args.currentRequest.push({ name: 'Authorization', value: `Bearer ${this.authToken}` });
}
}imageUploading
Triggers when an image upload begins.
Event Type: EmitType<UploadingEventArgs>
Example:
onImageUploading(args: UploadingEventArgs): void {
this.showUploadProgress = true;
}imageUploadSuccess
Triggers when an image has been successfully uploaded to the server.
Event Type: EmitType<ImageSuccessEventArgs>
Event Arguments:
detectImageSource(ImageInputSource): Detected image sourcee(object): Original event argumentselement(HTMLElement): Image elementfile(FileInfo): File detailsname(string): Event nameoperation(string): Operation performedresponse(ResponseEventArgs): Response detailsstatusText(string): Status text
Example:
import { ImageSuccessEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onImageUploadSuccess(args: ImageSuccessEventArgs): void {
console.log('Image uploaded:', args.file.name);
this.uploadedImages.push({
name: args.file.name,
url: args.element.getAttribute('src')
});
}imageUploadFailed
Triggers when there is an error during image upload.
Event Type: EmitType<ImageFailedEventArgs>
Event Arguments:
file(FileInfo): File that failed to uploadname(string): Event nameoperation(string): Operation attemptedresponse(ResponseEventArgs): Response detailsstatusText(string): Status texte(object): Original event
Example:
import { ImageFailedEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onImageUploadFailed(args: ImageFailedEventArgs): void {
console.error('Image upload failed:', args.file.name);
alert(`Failed to upload ${args.file.name}: ${args.statusText}`);
}imageSelected
Triggers when an image is selected or dragged into the insert image dialog.
Event Type: EmitType<SelectedEventArgs>
Example:
onImageSelected(args: SelectedEventArgs): void {
console.log('Image selected for upload');
}imageRemoving
Triggers when a selected image is removed from the insert image dialog.
Event Type: EmitType<RemovingEventArgs>
Example:
onImageRemoving(args: RemovingEventArgs): void {
console.log('Image being removed from upload queue');
}beforeFileUpload
Triggers before media (audio/video) upload process starts.
Event Type: EmitType<BeforeUploadEventArgs>
Example:
onBeforeFileUpload(args: BeforeUploadEventArgs): void {
console.log('Media file upload starting');
}fileUploading
Triggers when media begins uploading.
Event Type: EmitType<UploadingEventArgs>
Example:
onFileUploading(args: UploadingEventArgs): void {
console.log('Uploading media file...');
}fileUploadSuccess
Triggers when media has been successfully uploaded.
Event Type: EmitType<Object>
Example:
onFileUploadSuccess(): void {
console.log('Media uploaded successfully');
}fileUploadFailed
Triggers when there is an error during media upload.
Event Type: EmitType<Object>
Example:
onFileUploadFailed(): void {
console.error('Media upload failed');
}fileSelected
Triggers when media is selected or dragged into the insert media dialog.
Event Type: EmitType<SelectedEventArgs>
Example:
onFileSelected(args: SelectedEventArgs): void {
console.log('Media file selected');
}fileRemoving
Triggers when selected media is removed from the insert media dialog.
Event Type: EmitType<RemovingEventArgs>
Example:
onFileRemoving(args: RemovingEventArgs): void {
console.log('Media file being removed');
}---
Document Import/Export Events
documentExporting
Triggers just before PDF or Word export requests are dispatched, allowing customization of the outgoing request.
Event Type: EmitType<ExportingEventArgs>
Event Arguments:
currentRequest([key: string]: string)[]: HTTP headers to merge into the export requestcustomFormData([key: string]: Object)[]: Form-data entries to includeexportType(ExportDocumentType): Export type (PDF or Word)
Example:
import { ExportingEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `<ejs-richtexteditor (documentExporting)="onDocumentExporting($event)"></ejs-richtexteditor>`,
providers: [ImportExportService]
})
export class AppComponent {
onDocumentExporting(args: ExportingEventArgs): void {
console.log('Exporting to:', args.exportType);
// Add authentication headers
args.currentRequest = args.currentRequest || [];
args.currentRequest.push({
name: 'Authorization',
value: `Bearer ${this.authToken}`
});
// Add custom form data
args.customFormData = args.customFormData || [];
args.customFormData.push({
name: 'userId',
value: this.currentUser.id
});
}
}wordImporting
Triggers when a Word document import is initiated, allowing customization of the outgoing request.
Event Type: EmitType<UploadingEventArgs>
Example:
onWordImporting(args: UploadingEventArgs): void {
console.log('Importing Word document');
args.currentRequest = args.currentRequest || [];
args.currentRequest.push({
name: 'Authorization',
value: `Bearer ${this.authToken}`
});
}Getting Started with Syncfusion Angular Rich Text Editor
Table of Contents
- Installation
- Module Options: RichTextEditorModule vs RichTextEditorAllModule
- Service Injection
- CSS Theme Imports
- Standalone Component Setup
- NgModule Setup
- Basic Component with Value Binding
---
Installation
npm install @syncfusion/ej2-angular-richtexteditor---
Module Options
Two module imports are available:
| Module | When to use |
|---|---|
RichTextEditorModule | Use when you manually inject only the services you need (recommended for smaller bundles) |
RichTextEditorAllModule | Use when you want all services available without listing them individually (convenient, larger bundle) |
---
Service Injection
The RTE uses a modular service architecture. Only inject the services your editor needs — unused services increase bundle size unnecessarily.
// Core services for a standard HTML editor
import {
ToolbarService, // Required for toolbar rendering
HtmlEditorService, // Required for HTML editing mode
LinkService, // Required for hyperlink insertion
ImageService, // Required for image insertion
QuickToolbarService, // Required for context toolbars (image, link, table)
TableService, // Required for table insertion
PasteCleanupService, // Required for paste cleanup
} from '@syncfusion/ej2-angular-richtexteditor';Full services reference:
| Service | What it enables |
|---|---|
ToolbarService | Toolbar rendering |
HtmlEditorService | HTML editing mode |
MarkdownEditorService | Markdown editing mode |
LinkService | Hyperlink insertion |
ImageService | Image insertion |
TableService | Table insertion |
QuickToolbarService | Floating context toolbars |
ResizeService | Resizable editor handle |
FileManagerService | File browser dialog |
PasteCleanupService | Paste sanitization |
ClipboardCleanupService | Copy/cut clipboard cleanup |
ImportExportService | Word/PDF import & export |
AIAssistantService | AI assistant integration |
EmojiPickerService | Emoji picker |
SlashMenuService | Slash command menu |
CodeBlockService | Inline code block formatting |
AudioService | Audio insertion |
VideoService | Video insertion |
FormatPainterService | Format painting |
AutoFormatService | Auto Markdown-to-HTML conversion |
CountService | Live character counting |
---
CSS Theme Imports
Add to src/styles.css. Choose one theme set:
Material 3 (recommended):
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/material3.css';Tailwind 3:
@import '../node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-icons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/tailwind3.css';Other available themes: bootstrap5, bootstrap5.3, fluent2, highcontrast. Replace material3 with your chosen theme name across all imports.
---
Standalone Component Setup
import { Component } from '@angular/core';
import { RichTextEditorModule } from '@syncfusion/ej2-angular-richtexteditor';
import {
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor [(value)]="content"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public content: string = '<p>Start editing here...</p>';
}Key: Services go in the componentprovidersarray, not inimports.
---
NgModule Setup
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RichTextEditorModule } from '@syncfusion/ej2-angular-richtexteditor';
import {
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, RichTextEditorModule],
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService],
bootstrap: [AppComponent]
})
export class AppModule {}// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `<ejs-richtexteditor [(value)]="content"></ejs-richtexteditor>`
})
export class AppComponent {
public content: string = '<p>Start editing here...</p>';
}---
Basic Component with Value Binding
Two-way binding (recommended for most use cases):
@Component({
template: `<ejs-richtexteditor [(value)]="htmlContent"></ejs-richtexteditor>`
})
export class AppComponent {
public htmlContent: string = '<p>Initial content</p>';
}One-way binding + change event (when you need to intercept changes):
import { ChangeEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
template: `
<ejs-richtexteditor [value]="htmlContent" (change)="onContentChange($event)">
</ejs-richtexteditor>
`
})
export class AppComponent {
public htmlContent: string = '<p>Initial content</p>';
onContentChange(args: ChangeEventArgs): void {
// args.value contains the updated HTML string
this.htmlContent = args.value;
console.log('Content changed:', args.value);
}
}Thechangeevent fires only when the editor loses focus and there were actual changes — not on every keystroke. Useinputevent for keystroke-level updates.
---
Run the Application
ng serve --openGlobalization, Localization & RTL
Reference for localizing RTE UI strings using L10n, enabling right-to-left (RTL) mode, and customizing locale-specific behavior.
---
Table of Contents
- Localization with L10n
- Default locale keys (en-US)
- Applying a locale
- Example: German (de-DE)
- Right-to-Left (RTL) Mode
- Enabling RTL
- CSS customization in RTL mode
- Key Notes
---
Localization with L10n
The RTE uses the L10n utility from @syncfusion/ej2-base to load translated strings. By default it uses US English (en-US). Load your locale strings before bootstrapping the Angular app (i.e., in main.ts or at the top of the component file before @Component).
Default locale keys (en-US)
The complete set of keys for "richtexteditor" (partial list of commonly customized keys):
| Key | Default Value |
|---|---|
bold | "Bold" |
italic | "Italic" |
underline | "Underline" |
strikethrough | "Strikethrough" |
fontName | "Font Name" |
fontSize | "Font Size" |
fontColor | "Font Color" |
backgroundColor | "Background Color" |
alignments | "Alignments" |
orderedList | "Numbered List" |
unorderedList | "Bulleted List" |
createLink | "Insert Link" |
image | "Insert Image" |
audio | "Insert Audio" |
video | "Insert Video" |
undo | "Undo" |
redo | "Redo" |
sourcecode | "Code View" |
fullscreen | "Maximize" |
formats | "Formats" |
clearFormat | "Clear Format" |
print | "Print" |
pasteFormat | "Paste Format" |
plainText | "Plain Text" |
keepFormat | "Keep" |
cleanFormat | "Clean" |
emojiPicker | "Emoji Picker" |
formatPainter | "Format Painter" |
fileManager | "File Manager" |
mergecells | "Merge cells" |
inserttablebtn | "Insert Table" |
For the full list of ~150+ keys, see the source locale object in docs/rich-text-editor/globalization.md.
Applying a locale
Set the locale property on the component and call L10n.load() with your translations before the component initializes:
import { enableRipple, L10n } from '@syncfusion/ej2-base';
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarService, LinkService,
ImageService, HtmlEditorService, QuickToolbarService,
TableService, PasteCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
enableRipple(true);
L10n.load({
'de-DE': {
'richtexteditor': {
bold: 'Fett gedruckt',
italic: 'Kursiv',
underline: 'Unterstreichen',
// ... add remaining keys
}
}
});
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id="editor" locale="de-DE"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
QuickToolbarService, TableService, PasteCleanupService]
})
export class AppComponent {}Example: German (de-DE)
Key translations (representative subset):
L10n.load({
'de-DE': {
'richtexteditor': {
alignments: 'Ausrichtungen',
justifyLeft: 'Linksbündig',
justifyCenter: 'Zentriert',
justifyRight: 'Rechts ausrichten',
justifyFull: 'Justify ausrichten',
fontName: 'Schriftartenname',
fontSize: 'Schriftgrösse',
bold: 'Fett gedruckt',
italic: 'Kursiv',
underline: 'Unterstreichen',
strikethrough: 'Durchgestrichen',
clearFormat: 'Format löschen',
clearAll: 'Alles löschen',
undo: 'Rückgängig machen',
redo: 'Wiederholen',
createLink: 'Hyperlink einfügen',
image: 'Bild einfügen',
audio: 'Audio einfügen',
video: 'Video einfügen',
fullscreen: 'Maximieren',
formats: 'Formate',
sourcecode: 'Code-Ansicht',
inserttablebtn: 'Tabelle einfügen',
pasteFormat: 'Format einfügen',
plainText: 'Einfacher Text',
cleanFormat: 'Sauber',
keepFormat: 'Behalten',
dialogInsert: 'Einfügen',
dialogCancel: 'Abbrechen',
dialogUpdate: 'Aktualisieren',
// Slash menu items
slashMenuItemHeadingOneText: 'Überschrift 1',
slashMenuItemHeadingTwoText: 'Überschrift 2',
slashMenuItemParagraphText: 'Absatz',
slashMenuItemTableText: 'Tisch',
slashMenuItemLinkText: 'Link',
}
}
});Tip: If a key is missing from your locale object, the RTE falls back to the English (en-US) default for that string.---
Right-to-Left (RTL) Mode
Enabling RTL
Set [enableRtl]="true" to render the editor with right-to-left layout. This is independent of locale — you can have an English locale with RTL layout or vice versa.
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarService, LinkService,
ImageService, HtmlEditorService, QuickToolbarService,
TableService, PasteCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id="editor" [enableRtl]="true"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
QuickToolbarService, TableService, PasteCleanupService]
})
export class AppComponent {}CSS customization in RTL mode
When RTL is enabled, the class e-rtl is added to the root element. Use it to apply RTL-specific styles:
.e-richtexteditor .e-rtl {
background-color: antiquewhite;
}---
Key Notes
L10n.load()must be called before the component is instantiated (top of the module ormain.ts)localeproperty accepts IETF language tag strings (e.g.,"de-DE","fr-FR","ar-AE")- RTL (
enableRtl) andlocaleare independent properties — changing one does not affect the other - For Arabic/Hebrew, combine both:
locale="ar-AE"+[enableRtl]="true" - Slash menu items, emoji picker, and AI assistant strings are all localizable via their own locale keys
- Missing locale keys fall back gracefully to
en-US
Import, Export & Spell Check
Reference for importing content from Word documents, exporting to PDF/Word, spell/grammar check integration via WProofreader, and required CSS for external export tools.
---
Table of Contents
- Import from Word
- Using hidden Uploader component
- Using built-in ImportWord toolbar item
- Secure import with authentication
- Export to PDF and Word
- Custom toolbar buttons approach
- Built-in ExportWord and ExportPdf toolbar items
- Secure export with authentication
- CSS for External Export Tools
- Spell and Grammar Check (WProofreader)
---
Import from Word
Using hidden Uploader component
Attach a hidden ejs-uploader to accept .docx/.doc/.rtf files and POST them to an ASP.NET Core endpoint that converts them to HTML:
import { Component, ViewChild } from '@angular/core';
import {
AsyncSettingsModel, SuccessEventArgs,
UploaderComponent, UploaderModule
} from '@syncfusion/ej2-angular-inputs';
import {
RichTextEditorModule, RichTextEditorComponent,
ToolbarSettingsModel, ToolbarService, LinkService,
ImageService, HtmlEditorService, QuickToolbarService,
TableService, PasteCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule, UploaderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor #editor [toolbarSettings]="tools" [(value)]="value"></ejs-richtexteditor>
<ejs-uploader #uploadObj
allowedExtensions=".docx,.doc,.rtf"
[asyncSettings]="asyncSettings"
(success)="onUploadSuccess($event)"
style="display: none;">
</ejs-uploader>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
QuickToolbarService, TableService, PasteCleanupService]
})
export class AppComponent {
@ViewChild('editor') editorObj!: RichTextEditorComponent;
@ViewChild('uploadObj') uploadObj!: UploaderComponent;
value = '<p>Start editing or import a Word document.</p>';
private hostUrl = 'url';
asyncSettings: AsyncSettingsModel = {
saveUrl: this.hostUrl + 'api/RichTextEditor/ImportFromWord'
};
tools: ToolbarSettingsModel = {
items: [{
tooltipText: 'Import from Word',
template: `<button class="e-tbar-btn e-control e-btn e-lib e-icon-btn" tabindex="-1" style="width:100%">
<span class="e-icons e-rte-import-doc e-btn-icon"></span></button>`,
click: this.importFromWord.bind(this)
}]
};
onUploadSuccess(args: SuccessEventArgs): void {
const html = ((args.e as ProgressEvent).currentTarget as XMLHttpRequest).response;
this.editorObj.executeCommand('insertHTML', html, { undo: true });
}
importFromWord(): void {
this.uploadObj.element.click();
}
}Server-side handler (ASP.NET Core):
- Accepts
IList<IFormFile> UploadFiles - Uses Syncfusion
DocIOto parse the file and convert to HTML - Returns sanitized HTML string in the response body
Using built-in ImportWord toolbar item
For a simpler setup, use the built-in 'ImportWord' toolbar item with the importWord property:
@Component({
template: `<ejs-richtexteditor
[toolbarSettings]="toolbarSettings"
[importWord]="importWord">
</ejs-richtexteditor>`,
providers: [ToolbarService, QuickToolbarService, LinkService,
ImageService, HtmlEditorService]
})
export class AppComponent {
private hostUrl = 'url';
toolbarSettings = { items: ['ImportWord'] };
importWord = {
serviceUrl: this.hostUrl + 'api/RichTextEditor/ImportFromWord'
};
}Secure import with authentication
Use the wordImporting event to inject auth headers and custom form data before the upload POST:
import { UploadingEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
template: `<ejs-richtexteditor
[toolbarSettings]="toolbarSettings"
[importWord]="importWord"
[wordImporting]="onWordImport">
</ejs-richtexteditor>`,
providers: [ToolbarService, QuickToolbarService, LinkService,
ImageService, HtmlEditorService]
})
export class AppComponent {
private hostUrl = 'url';
toolbarSettings = { items: ['ImportWord'] };
importWord = { serviceUrl: this.hostUrl + 'api/RichTextEditor/ImportFromWord' };
onWordImport = (args: UploadingEventArgs) => {
args.currentRequest.setRequestHeader('Authorization', 'Bearer <token>');
args.customFormData = [{ userId: '1234' }];
};
}---
Export to PDF and Word
Custom toolbar buttons approach
Use fetch to POST the editor's HTML to a server endpoint that returns a file blob:
@Component({
template: `<ejs-richtexteditor #editor [toolbarSettings]="tools" [enableXhtml]="true"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
QuickToolbarService, TableService, PasteCleanupService]
})
export class AppComponent {
@ViewChild('editor') editorObj!: RichTextEditorComponent;
private hostUrl = 'url';
tools: ToolbarSettingsModel = {
items: [
{
tooltipText: 'Export to Word',
template: `<button class="e-tbar-btn e-control e-btn e-lib e-icon-btn" tabindex="-1" style="width:100%">
<span class="e-icons e-rte-export-doc e-btn-icon"></span></button>`,
click: this.exportToWord.bind(this)
},
{
tooltipText: 'Export to PDF',
template: `<button class="e-tbar-btn e-control e-btn e-lib e-icon-btn" tabindex="-1" style="width:100%">
<span class="e-icons e-rte-export-pdf e-btn-icon"></span></button>`,
click: this.exportToPDF.bind(this)
}
]
};
exportToWord(): void {
const html = `<html><head></head><body>${this.editorObj.getHtml()}</body></html>`;
fetch(this.hostUrl + 'api/RichTextEditor/ExportToDocx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ html })
})
.then(r => r.blob())
.then(blob => this.downloadBlob(blob, 'Result.docx'));
}
exportToPDF(): void {
const html = `<html><head></head><body>${this.editorObj.getHtml()}</body></html>`;
fetch(this.hostUrl + 'api/RichTextEditor/ExportToPdf', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ html })
})
.then(r => r.blob())
.then(blob => this.downloadBlob(blob, 'Sample.pdf'));
}
private downloadBlob(blob: Blob, filename: string): void {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = filename;
document.body.appendChild(a); a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
}Built-in ExportWord and ExportPdf toolbar items
Use the built-in toolbar items with the exportWord and exportPdf properties:
@Component({
template: `<ejs-richtexteditor
[toolbarSettings]="toolbarSettings"
[exportWord]="exportWord"
[exportPdf]="exportPdf">
</ejs-richtexteditor>`,
providers: [ToolbarService, QuickToolbarService, LinkService, ImageService, HtmlEditorService]
})
export class AppComponent {
private hostUrl = 'url';
toolbarSettings = { items: ['ExportWord', 'ExportPdf'] };
exportWord = {
serviceUrl: this.hostUrl + 'api/RichTextEditor/ExportToDocx',
fileName: 'RichTextEditor.docx',
stylesheet: '.e-rte-content { font-size: 1em; font-weight: 400; margin: 0; }'
};
exportPdf = {
serviceUrl: this.hostUrl + 'api/RichTextEditor/ExportToPdf',
fileName: 'RichTextEditor.pdf',
stylesheet: '.e-rte-content { font-size: 1em; font-weight: 400; margin: 0; }'
};
}Secure export with authentication
Use the documentExporting event to add auth headers:
import { ExportingEventArgs } from '@syncfusion/ej2-angular-richtexteditor';
onDocumentExporting = (args: ExportingEventArgs) => {
args.currentRequest = [{ Authorization: 'Bearer <token>' }];
args.customFormData = [{ userId: '1234' }];
};---
CSS for External Export Tools
When exporting RTE content via external (non-Syncfusion) tools, the editor's internal styles are not automatically included. Apply the following CSS to the export container to preserve formatting. Add class e-rte-content to the container element.
.e-rte-content { font-size: 1em; font-weight: 400; margin: 0; }
html { height: auto; margin: 0; }
body { margin: 0; color: #333; word-wrap: break-word; }
.e-content {
min-height: 100px; outline: 0 solid transparent; padding: 16px;
font-weight: normal; line-height: 1.5; font-size: 14px;
font-family: "Roboto", "Segoe UI", "sans-serif";
}
p { margin: 0 0 10px 0; }
h1 { font-size: 2.857em; font-weight: 600; line-height: 1.2; margin: 10px 0; }
h2 { font-size: 2.285em; font-weight: 600; line-height: 1.2; margin: 10px 0; }
h3 { font-size: 2em; font-weight: 600; line-height: 1.2; margin: 10px 0; }
blockquote { margin: 10px 0; padding-left: 12px; border-left: 2px solid #5c5c5c; }
table { margin-bottom: 10px; border-collapse: collapse; }
th { background-color: rgba(157,157,157,.15); border: 1px solid #BDBDBD; padding: 2px 5px; }
td { border: 1px solid #BDBDBD; padding: 2px 5px; }---
Spell and Grammar Check (WProofreader)
The RTE does not have built-in spell check. Integrate the third-party WProofreader library from WebSpellChecker for real-time spelling and grammar checking.
Features: Real-time spell checking, multi-language support, custom dictionary, grammar suggestions.
Installation
npm install @webspellchecker/wproofreader-sdk-jsIntegration
Point WProofreader's container to the RTE's inputElement after the view initializes:
import { Component, AfterViewInit, ViewChild } from '@angular/core';
import {
RichTextEditorModule, RichTextEditorComponent,
ToolbarService, LinkService, ImageService,
HtmlEditorService, QuickToolbarService,
TableService, PasteCleanupService
} from '@syncfusion/ej2-angular-richtexteditor';
import WProofreader from '@webspellchecker/wproofreader-sdk-js';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor #spellEditor id="editor" [value]="value"></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
QuickToolbarService, TableService, PasteCleanupService]
})
export class AppComponent implements AfterViewInit {
@ViewChild('spellEditor') spellEditor!: RichTextEditorComponent;
value = '<p>Enter your text here to check spelling and grammar.</p>';
ngAfterViewInit(): void {
WProofreader.init({
container: this.spellEditor.inputElement,
lang: 'en_US',
serviceId: '<your-service-id>' // Get from webspellchecker.com
});
}
}Note:serviceIdis a WProofreader license key obtained from webspellchecker.com. Thecontainermust be the RTE'sinputElement(the actual editable area), not the host element.
Inserting Images, Video, Audio & Files in Syncfusion Angular Rich Text Editor
Table of Contents
- Images Overview
- Image Upload to Server
- Image Save Format (Blob vs Base64)
- Image Size Restriction
- Secure Image Upload with Auth Header
- Video Insertion
- Audio Insertion
- File Browser Integration
- Common insertImageSettings Reference
---
Images Overview
Add Image to toolbar items. Requires ImageService in providers.
The image toolbar button opens a dialog that allows:
- Inserting by URL (web source)
- Uploading from local machine
- Using the file browser (if configured)
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarSettingsModel, QuickToolbarSettingsModel,
ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[toolbarSettings]="tools"
[quickToolbarSettings]="quickTools">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', '|', 'Image', '|', 'Undo', 'Redo']
};
public quickTools: QuickToolbarSettingsModel = {
image: ['Replace', 'Align', 'WrapText', 'Caption', 'Remove',
'InsertLink', 'OpenImageLink', '|',
'EditImageLink', 'RemoveImageLink', 'Display', 'AltText', 'Dimension']
};
}---
Image Upload to Server
Use insertImageSettings.saveUrl to configure the server upload endpoint. path sets the public path for the saved images.
import { Component } from '@angular/core';
import {
RichTextEditorModule, ImageSettingsModel,
ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[toolbarSettings]="tools"
[insertImageSettings]="imageSettings">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService]
})
export class AppComponent {
public tools = { items: ['Image'] };
public imageSettings: ImageSettingsModel = {
saveUrl: '/api/Home/SaveImage',
removeUrl: '/api/Home/RemoveImage',
path: '/Uploads/'
};
}ASP.NET Core server-side handler:
[AcceptVerbs("Post")]
public void SaveImage(IList<IFormFile> UploadFiles)
{
foreach (IFormFile file in UploadFiles)
{
string filename = hostingEnv.WebRootPath + "\\Uploads\\" +
ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
if (!Directory.Exists(hostingEnv.WebRootPath + "\\Uploads"))
Directory.CreateDirectory(hostingEnv.WebRootPath + "\\Uploads");
if (!System.IO.File.Exists(filename))
{
using FileStream fs = System.IO.File.Create(filename);
file.CopyTo(fs);
Response.StatusCode = 200;
}
}
}---
Image Save Format
Images can be saved as Blob (default) or Base64:
import { ImageSettingsModel } from '@syncfusion/ej2-angular-richtexteditor';
public imageSettings: ImageSettingsModel = {
saveFormat: 'Base64' // or 'Blob' (default)
};- Blob: Generates a blob URL (
blob:http://...) — use with server upload - Base64: Embeds the image directly as a base64 string — no upload needed but increases content size
---
Image Size Restriction
Restrict uploads to a maximum file size (bytes). Default is 30,000,000 bytes (30 MB):
public imageSettings: ImageSettingsModel = {
maxFileSize: 2000000 // 2 MB limit
};---
Secure Image Upload with Auth Header
Add authorization headers to the upload request via the imageUploading event:
import { UploadingEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
template: `
<ejs-richtexteditor
[insertImageSettings]="imageSettings"
(imageUploading)="onImageUploading($event)">
</ejs-richtexteditor>
`
})
export class AppComponent {
public imageSettings: ImageSettingsModel = {
saveUrl: '/api/SaveFiles',
path: '/Files/'
};
onImageUploading(args: UploadingEventArgs): void {
args.currentRequest.setRequestHeader('Authorization', 'Bearer YOUR_TOKEN');
}
}---
Video Insertion
Requires VideoService in providers and Video in toolbar items:
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarSettingsModel, QuickToolbarSettingsModel,
ToolbarService, LinkService, ImageService, HtmlEditorService,
VideoService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[toolbarSettings]="tools"
[quickToolbarSettings]="quickTools">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
VideoService, QuickToolbarService]
})
export class AppComponent {
public tools: ToolbarSettingsModel = { items: ['Video'] };
public quickTools: QuickToolbarSettingsModel = {
showOnRightClick: true,
video: ['VideoReplace', 'VideoAlign', 'VideoRemove', 'VideoLayoutOption', 'VideoDimension']
};
}---
Audio Insertion
Requires AudioService in providers and Audio in toolbar items:
import {
RichTextEditorModule, ToolbarService, LinkService, ImageService,
HtmlEditorService, AudioService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
// In component:
// providers: [..., AudioService]
// template: <ejs-richtexteditor [toolbarSettings]="tools" [quickToolbarSettings]="quickTools">
public tools: ToolbarSettingsModel = { items: ['Audio'] };
public quickTools: QuickToolbarSettingsModel = {
showOnRightClick: true,
audio: ['AudioReplace', 'Remove', 'AudioLayoutOption']
};---
File Browser Integration
Allows browsing and selecting existing server files. Requires FileManagerService.
import { Component } from '@angular/core';
import {
RichTextEditorModule, ToolbarSettingsModel, FileManagerSettingsModel,
ToolbarService, LinkService, ImageService, HtmlEditorService,
FileManagerService, QuickToolbarService
} from '@syncfusion/ej2-angular-richtexteditor';
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-richtexteditor
[toolbarSettings]="tools"
[fileManagerSettings]="fileManagerSettings">
</ejs-richtexteditor>
`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService,
FileManagerService, QuickToolbarService]
})
export class AppComponent {
public tools: ToolbarSettingsModel = {
items: ['Image', 'FileManager']
};
public fileManagerSettings: FileManagerSettingsModel = {
enable: true,
path: '/Pictures/Food',
ajaxSettings: {
url: '/api/FileManager/FileOperations',
getImageUrl: '/api/FileManager/GetImage',
uploadUrl: '/api/FileManager/Upload',
downloadUrl: '/api/FileManager/Download'
}
};
}---
Common insertImageSettings Reference
| Property | Type | Default | Description |
|---|---|---|---|
saveUrl | string | — | Server endpoint to upload images |
removeUrl | string | — | Server endpoint to delete uploaded images |
path | string | — | Public path prefix for inserted image URLs |
saveFormat | `'Blob' \ | 'Base64'` | 'Blob' |
maxFileSize | number | 30000000 | Max upload size in bytes |
width | string | 'auto' | Default width for inserted images |
height | string | 'auto' | Default height for inserted images |
allowedTypes | string[] | ['.jpeg', '.jpg', '.png'] | Allowed file extensions |