Build a rich text editor with Angular + NgxTraak

Hello everyone! In this article, I’ll walk you through how to integrate highly customizable WYSIWYG editors into your Angular applications using the library I'm currently developing — ngx-traak. Setup First, install the required dependencies: npm i ngx-traak Since ngx-traak is built on top of ProseMirror, you also need to install several ProseMirror packages: npm i prosemirror-model prosemirror-view prosemirror-commands prosemirror-state prosemirror-inputrules prosemirror-schema-list prosemirror-keymap Configuring the editor We’ll declare the editor inside the AppComponent of your Angular application. Start by importing the necessary nodes, marks, the TraakEditorComponent, the MenuComponent, and the TraakConfiguration object: // app.component.ts import { BulletList, ListItem, OrderedList, Paragraph, TraakConfiguration, TraakEditorComponent, Heading, Bold, Italic, StrikeThrough, MenuComponent, } from "ngx-traak"; Next, register the components in the imports array of your component’s metadata: // app.component.ts @Component({ selector: "app-root", imports: [RouterOutlet, TraakEditorComponent, MenuComponent], templateUrl: "./app.component.html", styleUrl: "./app.component.css", }) Now, you can define the TraakConfiguration object, which is the configuration used for you to decide, what node, marks, and starter node you want to include in the editor. export class AppComponent { title = "ngx-traak-demo"; config: TraakConfiguration = { nodes: [Paragraph, Heading, ListItem, BulletList, OrderedList], marks: [Bold, Italic, StrikeThrough], starterNode: "Hello World !", }; } In your component’s template (app.component.html), declare the component and pass the configuration object you defined earlier using property binding: Interacting with the editor You might now be wondering: how can I interact with the editor, trigger commands, or manipulate its content programmatically? The answer is Plugins. In ngx-traak, plugins are simply Angular components that extend the TraakPlugin base class. Because of this, each plugin automatically has access to the editor instance through the inherited properties and methods. This design makes it easy to: Create your own fully customized Angular components. Apply your own styling using Angular’s standard tools (CSS/SCSS). Seamlessly integrate third-party Angular libraries directly into the editor's environment. This means you can build UI components like toolbars, sidebars, popups, or menus — fully powered by Angular — and deeply connected to the editor. Let us create a button which adds a bullet_list node at the current cursor position, (which could eventually be a part of a whole menu for example). ng g c button-plugin First, in the components metadata, provide the TraakPlugin interface with your component so it can be picked up as a plugin : providers: [{ provide: TraakPlugin, useExisting: forwardRef(() => ButtonPluginComponent)},] In the template, let's create a button and bind the click event to an add bullet list method : Add Bullet list The implementation of this method would look something like this : addBulletList(event: MouseEvent) { event.preventDefault(); this.editor.commands.addNode("hello" }.commit(); The TraakEditorComponent exposes a commands attribute, which provides a convenient way to build and chain editor operations using a fluent API. Here’s how it works: The commands object creates a transaction behind the scenes. You can chain multiple commands (e.g., inserting nodes, formatting text). To apply all the changes to the editor, you must explicitly call the .commit() method at the end. To see more plugins example, you can check the source code of the builtin plugins which come with the library. Back in your app component template, pass the plugin to the traak-editor : Contributing If you find ngx-traak helpful, please consider giving the project a star on github. The library is an early development stages, and we welcome all contributions to help it grow.

Apr 28, 2025 - 20:22
 0
Build a rich text editor with Angular + NgxTraak

Hello everyone!
In this article, I’ll walk you through how to integrate highly customizable WYSIWYG editors into your Angular applications using the library I'm currently developing — ngx-traak.

Setup

First, install the required dependencies:

npm i ngx-traak 

Since ngx-traak is built on top of ProseMirror, you also need to install several ProseMirror packages:

npm i prosemirror-model prosemirror-view prosemirror-commands prosemirror-state prosemirror-inputrules prosemirror-schema-list prosemirror-keymap

Configuring the editor

We’ll declare the editor inside the AppComponent of your Angular application.

Start by importing the necessary nodes, marks, the TraakEditorComponent, the MenuComponent, and the TraakConfiguration object:

// app.component.ts
import {
  BulletList,
  ListItem,
  OrderedList,
  Paragraph,
  TraakConfiguration,
  TraakEditorComponent,
  Heading,
  Bold,
  Italic,
  StrikeThrough,
  MenuComponent,
} from "ngx-traak";

Next, register the components in the imports array of your component’s metadata:

// app.component.ts
@Component({
  selector: "app-root",
  imports: [RouterOutlet, TraakEditorComponent, MenuComponent],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})

Now, you can define the TraakConfiguration object, which is the configuration used for you to decide, what node, marks, and starter node you want to include in the editor.

export class AppComponent {
  title = "ngx-traak-demo";
  config: TraakConfiguration = {
    nodes: [Paragraph, Heading, ListItem, BulletList, OrderedList],
    marks: [Bold, Italic, StrikeThrough],
    starterNode: "Hello World !",
  };
}

In your component’s template (app.component.html), declare the component and pass the configuration object you defined earlier using property binding:

 [config]=config>

Interacting with the editor

You might now be wondering: how can I interact with the editor, trigger commands, or manipulate its content programmatically?

The answer is Plugins.
In ngx-traak, plugins are simply Angular components that extend the TraakPlugin base class.
Because of this, each plugin automatically has access to the editor instance through the inherited properties and methods.

This design makes it easy to:

  • Create your own fully customized Angular components.
  • Apply your own styling using Angular’s standard tools (CSS/SCSS).
  • Seamlessly integrate third-party Angular libraries directly into the editor's environment.

This means you can build UI components like toolbars, sidebars, popups, or menus — fully powered by Angular — and deeply connected to the editor.

Let us create a button which adds a bullet_list node at the current cursor position, (which could eventually be a part of a whole menu for example).

ng g c button-plugin

First, in the components metadata, provide the TraakPlugin interface with your component so it can be picked up as a plugin :

providers: [{ provide: TraakPlugin, useExisting: forwardRef(() => ButtonPluginComponent)},]

In the template, let's create a button and bind the click event to an add bullet list method :

 (click)="addBulletList($event)"> Add Bullet list 

The implementation of this method would look something like this :

addBulletList(event: MouseEvent) {
    event.preventDefault();
    this.editor.commands.addNode("hello"
}.commit();

The TraakEditorComponent exposes a commands attribute, which provides a convenient way to build and chain editor operations using a fluent API.
Here’s how it works:
The commands object creates a transaction behind the scenes. You can chain multiple commands (e.g., inserting nodes, formatting text). To apply all the changes to the editor, you must explicitly call the .commit() method at the end.

To see more plugins example, you can check the source code of the builtin plugins which come with the library.

Back in your app component template, pass the plugin to the traak-editor :

 [config]="config">  

Contributing

If you find ngx-traak helpful, please consider giving the project a star on github.
The library is an early development stages, and we welcome all contributions to help it grow.