Getting Started
Overview
Installation
Theme
Forms
Basic Inputs
Custom Inputs
Select
Components
Components
78
Micro Charts
Navigation
Libraries
Content Editor
Data View
Form Builder
Headless Editor
Image Designer
Kanban Board
PDF Builder
PDF Signer
PDF Viewer
Video Player
GitHub repositoryGitHub starsGitHub forksNgStarter UI npm versionNgStarter UI npm downloads per month

Getting Started

Headless Editor is part of @ngstarter-ui/components. After installing NgStarter UI, import the editor from its secondary entry point.

1. Provide an editor

provideNgsHeadlessEditor() creates an editor instance for the component that declares it and for its children. The editor is deliberately not a root singleton: every editor on a page has its own provider scope. Plugins are passed as features; basicTextEditorPlugin() registers paragraphs and the bold, italic, strike, and inline code marks.

import { Component, inject } from '@angular/core';
import {
  basicTextEditorPlugin,
  NgsHeadlessEditor,
  NgsHeadlessEditorSurface,
  provideNgsHeadlessEditor,
  withHeadlessEditorPlugin
} from '@ngstarter-ui/components/headless-editor';

@Component({
  selector: 'app-note-editor',
  imports: [NgsHeadlessEditorSurface],
  providers: [
    provideNgsHeadlessEditor(
      withHeadlessEditorPlugin(basicTextEditorPlugin())
    )
  ],
  templateUrl: './note-editor.html'
})
export class NoteEditor {
  readonly editor = inject(NgsHeadlessEditor);
}

2. Render a surface

Put ngsHeadlessEditorSurface on any element inside the same injector scope. The directive makes it editable, renders the document into it, and keeps native selection and the model in sync.

<div
  class="editor"
  ngsHeadlessEditorSurface
  ariaLabel="Note"
  placeholder="Write a note…">
</div>
Loading example…

3. Load and read content

The value of an editor is its JSON document. Load one with setDocument() and read the current one from the document signal. Use origin to tell user edits apart from documents you loaded yourself.

import { effect } from '@angular/core';
import { createNgsHeadlessEditorDocument } from '@ngstarter-ui/components/headless-editor';

export class NoteEditor {
  readonly editor = inject(NgsHeadlessEditor);

  constructor() {
    // Load initial content. History is reset by default.
    this.editor.setDocument(createNgsHeadlessEditorDocument('Hello!'));

    effect(() => {
      const document = this.editor.document();
      // 'external' means the document was loaded with setDocument().
      if (this.editor.origin() !== 'external') {
        this.save(document);
      }
    });
  }

  private save(document: NgsHeadlessEditorDocument): void {
    // send JSON to your API
  }
}

4. Style it

The surface has no styles. Block and mark elements are created by the directive, outside your component template, so they do not receive Angular emulated-encapsulation attributes. Style them from a class on the surface with ::ng-deep, from global styles, or with ViewEncapsulation.None. The placeholder is exposed as an attribute on the first empty block.

.editor {
  display: block;
  min-height: 8rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--ngs-color-border);
  border-radius: var(--ngs-radius-md);
  outline: none;

  &:focus {
    border-color: var(--ngs-color-primary);
  }

  ::ng-deep {
    p {
      margin: 0 0 0.5rem;
    }

    [data-ngs-headless-editor-placeholder] {
      position: relative;

      &::before {
        content: attr(data-ngs-headless-editor-placeholder);
        position: absolute;
        inset: 0 auto auto 0;
        color: var(--ngs-color-neutral-500);
        pointer-events: none;
      }
    }
  }
}

5. Add a toolbar

Bind any button to a command with ngsHeadlessEditorCommand. The directive keeps the text selection when the button is pressed and reflects the command state with the active class, aria-pressed, and disabled. See Commands and Toolbar.

<button ngsButton="outlined" [ngsHeadlessEditorCommand]="bold">Bold</button>
<button ngsButton="outlined" [ngsHeadlessEditorCommand]="italic">Italic</button>

<!-- in the component: readonly bold = NGS_HEADLESS_EDITOR_TOGGLE_BOLD; -->

Checklist

  • Provide the editor at component level, not in the application config.
  • Keep the surface and command buttons inside the providing component.
  • Register a plugin for every block and mark type your documents contain.
  • Treat documents as immutable: create new objects instead of mutating existing ones.
  • Style blocks through the surface class with ::ng-deep or global styles.