CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tiptap--core

Headless rich text editor built on ProseMirror with extensible architecture for building custom editors

94

1.00x
Overview
Eval results
Files

editor-core.mddocs/

Editor Core

The Editor class is the central component of @tiptap/core, managing the editor state, view, extensions, and providing the main API interface for interacting with the rich text editor.

Capabilities

Editor Constructor

Creates a new editor instance with the specified configuration options.

/**
 * Creates a new Tiptap editor instance
 * @param options - Configuration options for the editor
 */
constructor(options: Partial<EditorOptions>): Editor;

interface EditorOptions {
  /** The DOM element to mount the editor to */
  element?: Element;
  
  /** Initial content for the editor */
  content?: Content;
  
  /** Array of extensions to load */
  extensions?: Extensions;
  
  /** Whether to inject default CSS styles */
  injectCSS?: boolean;
  
  /** Initial focus position */
  autofocus?: FocusPosition;
  
  /** Whether the editor should be editable */
  editable?: boolean;
  
  /** Enable input rules (markdown-like shortcuts) */
  enableInputRules?: boolean;
  
  /** Enable paste rules */
  enablePasteRules?: boolean;
  
  /** Enable core extensions */
  enableCoreExtensions?: boolean;
  
  /** Callback fired before editor creation */
  onBeforeCreate?(props: EditorEvents['beforeCreate']): void;
  
  /** Callback fired after editor creation */
  onCreate?(props: EditorEvents['create']): void;
  
  /** Callback fired on content updates */
  onUpdate?(props: EditorEvents['update']): void;
  
  /** Callback fired on selection updates */
  onSelectionUpdate?(props: EditorEvents['selectionUpdate']): void;
  
  /** Callback fired on transactions */
  onTransaction?(props: EditorEvents['transaction']): void;
  
  /** Callback fired on focus */
  onFocus?(props: EditorEvents['focus']): void;
  
  /** Callback fired on blur */
  onBlur?(props: EditorEvents['blur']): void;
  
  /** Callback fired when editor is destroyed */
  onDestroy?(): void;
}

type Content = 
  | string
  | JSONContent
  | JSONContent[]
  | ProseMirrorNode
  | null;

type FocusPosition = 
  | boolean
  | 'start'
  | 'end'
  | 'all'
  | number
  | { from: number; to?: number };

Usage Examples:

import { Editor } from '@tiptap/core';

// Basic editor
const editor = new Editor({
  element: document.querySelector('#editor'),
  content: '<p>Hello World!</p>'
});

// Editor with callbacks
const editor = new Editor({
  element: document.querySelector('#editor'),
  content: '<p>Hello World!</p>',
  autofocus: 'end',
  editable: true,
  onUpdate: ({ editor }) => {
    console.log('Content updated:', editor.getHTML());
  },
  onFocus: ({ editor, event }) => {
    console.log('Editor focused');
  }
});

Core Properties

Essential properties for inspecting the editor state and configuration.

/** ProseMirror EditorView instance (proxied when unmounted) */
readonly view: EditorView;

/** Current ProseMirror EditorState */
readonly state: EditorState;

/** ProseMirror Schema built from extensions */
readonly schema: Schema;

/** Storage object shared between extensions */
readonly storage: Storage;

/** Whether the editor accepts input */
readonly isEditable: boolean;

/** Whether the editor currently has focus */
readonly isFocused: boolean;

/** Whether the editor content is empty */
readonly isEmpty: boolean;

/** Whether the editor has been destroyed */
readonly isDestroyed: boolean;

/** Whether the editor is fully initialized */
readonly isInitialized: boolean;

Lifecycle Management

Methods for managing the editor lifecycle - mounting, unmounting, and destroying.

/**
 * Mount the editor to a DOM element
 * @param element - Optional DOM element to mount to (uses constructor element if not provided)
 * @returns The editor instance for chaining
 */
mount(element?: Element): Editor;

/**
 * Unmount the editor from DOM while preserving the instance for potential remounting
 * @returns The editor instance for chaining
 */
unmount(): Editor;

/**
 * Permanently destroy the editor instance and clean up resources
 */
destroy(): void;

Usage Examples:

// Create editor without mounting
const editor = new Editor({
  content: '<p>Hello World!</p>'
});

// Mount later
editor.mount(document.querySelector('#editor'));

// Temporarily unmount (can be remounted)
editor.unmount();

// Mount to different element
editor.mount(document.querySelector('#another-editor'));

// Permanently destroy
editor.destroy();

Command Execution

Core command execution interface providing single commands, command chains, and command validation.

/** Access to all registered commands for single execution */
readonly commands: SingleCommands;

/**
 * Create a command chain for executing multiple commands in sequence
 * @returns ChainedCommands instance
 */
chain(): ChainedCommands;

/**
 * Create a command validation interface
 * @returns CanCommands instance for testing command executability
 */
can(): CanCommands;

interface SingleCommands {
  [commandName: string]: (attributes?: Record<string, any>) => boolean;
}

interface ChainedCommands {
  [commandName: string]: (attributes?: Record<string, any>) => ChainedCommands;
  /** Execute the command chain */
  run(): boolean;
}

interface CanCommands {
  [commandName: string]: (attributes?: Record<string, any>) => boolean;
}

Usage Examples:

// Single command execution
const success = editor.commands.setBold();

// Command chaining
editor
  .chain()
  .focus()
  .setBold()
  .setItalic()
  .insertContent('Bold and italic text')
  .run();

// Command validation
if (editor.can().setBold()) {
  editor.commands.setBold();
}

// Conditional chaining
editor
  .chain()
  .focus()
  .toggleBold()
  .insertContent(editor.can().setItalic() ? 'Can italicize' : 'Cannot italicize')
  .run();

Content Management

Methods for retrieving and setting editor content in various formats.

/**
 * Get editor content as HTML string
 * @returns HTML representation of the document
 */
getHTML(): string;

/**
 * Get editor content as JSON
 * @returns JSONContent representation of the document
 */
getJSON(): JSONContent;

/**
 * Get editor content as plain text
 * @param options - Text extraction options
 * @returns Plain text content
 */
getText(options?: GetTextOptions): string;

interface GetTextOptions {
  /** Include block separators (default: true) */
  blockSeparator?: string;
  /** Include text between given positions */
  from?: number;
  to?: number;
}

interface JSONContent {
  type?: string;
  attrs?: Record<string, any>;
  content?: JSONContent[];
  marks?: Array<{
    type: string;
    attrs?: Record<string, any>;
  }>;
  text?: string;
}

Usage Examples:

// Get content in different formats
const htmlContent = editor.getHTML();
// '<p><strong>Bold text</strong></p>'

const jsonContent = editor.getJSON();
// { type: 'doc', content: [{ type: 'paragraph', content: [...] }] }

const plainText = editor.getText();
// 'Bold text'

const textWithCustomSeparator = editor.getText({ 
  blockSeparator: ' | ' 
});

// Get text from specific range
const rangeText = editor.getText({ 
  from: 0, 
  to: 10 
});

Configuration Management

Methods for updating editor configuration and behavior at runtime.

/**
 * Update editor options
 * @param options - Partial options to update
 */
setOptions(options: Partial<EditorOptions>): void;

/**
 * Change the editable state of the editor
 * @param editable - Whether the editor should be editable
 */
setEditable(editable: boolean): void;

/**
 * Register a new ProseMirror plugin
 * @param plugin - ProseMirror plugin to register
 * @param handlePlugins - Optional function to handle plugin conflicts
 */
registerPlugin(
  plugin: Plugin, 
  handlePlugins?: (newPlugin: Plugin, existingPlugins: Plugin[]) => Plugin[]
): void;

/**
 * Unregister a ProseMirror plugin
 * @param nameOrKey - Plugin name or PluginKey to remove
 */
unregisterPlugin(nameOrKey: string | PluginKey): void;

Usage Examples:

// Update editor options
editor.setOptions({
  editable: false,
  onUpdate: ({ editor }) => {
    console.log('New update handler');
  }
});

// Toggle editable state
editor.setEditable(false); // Make read-only
editor.setEditable(true);  // Make editable again

// Register custom plugin
import { Plugin, PluginKey } from '@tiptap/pm/state';

const customPluginKey = new PluginKey('customPlugin');
const customPlugin = new Plugin({
  key: customPluginKey,
  // plugin implementation
});

editor.registerPlugin(customPlugin);

// Unregister plugin
editor.unregisterPlugin('customPlugin');
// or
editor.unregisterPlugin(customPluginKey);

State Inspection

Methods for inspecting the current editor state and selection.

/**
 * Get attributes of the current selection
 * @param nameOrType - Node/mark name, NodeType, or MarkType
 * @returns Attributes object
 */
getAttributes(nameOrType: string | NodeType | MarkType): Record<string, any>;

/**
 * Check if a node or mark is active in the current selection
 * @param name - Node or mark name to check
 * @param attributes - Optional attributes to match
 * @returns Whether the node/mark is active
 */
isActive(name: string, attributes?: Record<string, any>): boolean;

Usage Examples:

// Check if formatting is active
const isBold = editor.isActive('bold');
const isItalic = editor.isActive('italic');
const isHeading = editor.isActive('heading', { level: 1 });

// Get current attributes
const linkAttrs = editor.getAttributes('link');
// { href: 'https://example.com', target: '_blank' }

const headingAttrs = editor.getAttributes('heading');
// { level: 2 }

// Use in UI state
function BoldButton() {
  const isActive = editor.isActive('bold');
  return (
    <button 
      className={isActive ? 'active' : ''}
      onClick={() => editor.chain().focus().toggleBold().run()}
    >
      Bold
    </button>
  );
}

Content Querying

Advanced methods for querying and navigating the document structure.

/**
 * Create a NodePos instance at the specified position
 * @param pos - Document position
 * @returns NodePos for advanced position operations
 */
$pos(pos: number): NodePos;

/**
 * Find a single node matching the selector and attributes
 * @param selector - Node type selector
 * @param attributes - Optional attributes to match
 * @returns NodePos if found, null otherwise
 */
$node(selector: string, attributes?: Record<string, any>): NodePos | null;

/**
 * Find all nodes matching the selector and attributes
 * @param selector - Node type selector
 * @param attributes - Optional attributes to match
 * @returns Array of NodePos instances
 */
$nodes(selector: string, attributes?: Record<string, any>): NodePos[];

Usage Examples:

// Get position info
const pos = editor.$pos(10);
console.log(pos.node()); // Node at position
console.log(pos.parent); // Parent node

// Find specific nodes
const firstHeading = editor.$node('heading');
const h1 = editor.$node('heading', { level: 1 });

// Find all nodes of a type
const allParagraphs = editor.$nodes('paragraph');
const allImages = editor.$nodes('image');

// Advanced querying
const allH2WithClass = editor.$nodes('heading', { 
  level: 2, 
  class: 'special' 
});

// Navigate and modify
const heading = editor.$node('heading');
if (heading) {
  heading.content = 'Updated heading content';
  heading.setAttribute({ level: 2 });
}

NodePos Class

The NodePos class provides a DOM-like interface for navigating and manipulating nodes in the document. It offers powerful querying and modification capabilities similar to browser DOM APIs.

/**
 * Represents a position in the document with DOM-like navigation and manipulation capabilities
 */
class NodePos {
  /** The ProseMirror node at this position */
  readonly node: Node;
  
  /** The document position */
  readonly pos: number;
  
  /** The depth in the document tree */
  readonly depth: number;
  
  /** Node attributes */
  readonly attributes: Record<string, any>;
  
  /** Node text content */
  readonly textContent: string;
  
  /** Node size in the document */
  readonly size: number;
  
  /** Start position of the node */
  readonly from: number;
  
  /** End position of the node */
  readonly to: number;
  
  /** Node range (from/to positions) */
  readonly range: Range;
  
  /** Corresponding DOM element */
  readonly element: HTMLElement;
  
  /** Node content fragment */
  content: Fragment;
  
  // Navigation properties
  /** Parent node (null if at root) */
  readonly parent: NodePos | null;
  
  /** Previous sibling node */
  readonly before: NodePos | null;
  
  /** Next sibling node */
  readonly after: NodePos | null;
  
  /** Child nodes array */
  readonly children: NodePos[];
  
  /** First child node */
  readonly firstChild: NodePos | null;
  
  /** Last child node */
  readonly lastChild: NodePos | null;
  
  // Query methods
  /**
   * Find the closest ancestor matching the selector
   * @param selector - Node type to search for
   * @param attributes - Optional attributes to match
   * @returns Closest matching ancestor or null
   */
  closest(selector: string, attributes?: Record<string, any>): NodePos | null;
  
  /**
   * Find first descendant matching the selector
   * @param selector - Node type to search for
   * @param attributes - Optional attributes to match
   * @returns First matching descendant or null
   */
  querySelector(selector: string, attributes?: Record<string, any>): NodePos | null;
  
  /**
   * Find all descendants matching the selector
   * @param selector - Node type to search for
   * @param attributes - Optional attributes to match
   * @returns Array of matching descendants
   */
  querySelectorAll(selector: string, attributes?: Record<string, any>): NodePos[];
  
  // Modification methods
  /**
   * Set attributes on this node
   * @param attributes - Attributes to set
   */
  setAttribute(attributes: Record<string, any>): void;
}

interface Range {
  from: number;
  to: number;
}

Usage Examples:

// DOM-like navigation
const pos = editor.$pos(50);
const parent = pos.parent;
const children = pos.children;
const firstChild = pos.firstChild;

// Query for specific nodes
const heading = pos.querySelector('heading');
const allParagraphs = pos.querySelectorAll('paragraph');
const h1WithId = pos.querySelector('heading', { level: 1, id: 'main' });

// Navigate relationships
const nextHeading = pos.closest('heading')?.after?.querySelector('heading');

// Content manipulation
const paragraph = editor.$node('paragraph');
if (paragraph) {
  paragraph.content = 'New paragraph content';
  paragraph.setAttribute({ class: 'highlighted' });
}

// Range operations
const range = paragraph?.range;
if (range) {
  editor.commands.setTextSelection(range);
}

// Complex navigation
const headings = editor.$nodes('heading');
headings.forEach(heading => {
  const level = heading.attributes.level;
  console.log(`H${level}: ${heading.textContent}`);
  
  // Find all paragraphs under this heading
  const paragraphs = heading.querySelectorAll('paragraph');
  console.log(`Contains ${paragraphs.length} paragraphs`);
});

// Tree traversal
function walkNodes(nodePos: NodePos, callback: (node: NodePos) => void) {
  callback(nodePos);
  nodePos.children.forEach(child => walkNodes(child, callback));
}

walkNodes(editor.$pos(0), node => {
  console.log(`${node.node.type.name} at ${node.pos}`);
});

Install with Tessl CLI

npx tessl i tessl/npm-tiptap--core

docs

command-system.md

document-helpers.md

editor-core.md

extension-system.md

index.md

rule-systems.md

utilities.md

tile.json