or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-ckeditor--ckeditor5-paragraph

Paragraph feature for CKEditor 5 enabling core paragraph editing functionality.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@ckeditor/ckeditor5-paragraph@46.0.x

To install, run

npx @tessl/cli install tessl/npm-ckeditor--ckeditor5-paragraph@46.0.0

index.mddocs/

CKEditor 5 Paragraph Feature

The CKEditor 5 paragraph feature provides fundamental paragraph editing functionality for CKEditor 5. It implements the core paragraph element support that enables users to create, edit, and format paragraphs within the editor, serving as the foundation for rich text editing.

Package Information

  • Package Name: @ckeditor/ckeditor5-paragraph
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @ckeditor/ckeditor5-paragraph

Core Imports

import { Paragraph, ParagraphCommand, InsertParagraphCommand, ParagraphButtonUI } from "@ckeditor/ckeditor5-paragraph";

For CommonJS (not typical for CKEditor 5):

const { Paragraph, ParagraphCommand, InsertParagraphCommand, ParagraphButtonUI } = require("@ckeditor/ckeditor5-paragraph");

Basic Usage

import ClassicEditor from "@ckeditor/ckeditor5-editor-classic/src/classiceditor";
import { Paragraph, ParagraphButtonUI } from "@ckeditor/ckeditor5-paragraph";

ClassicEditor
  .create(document.querySelector('#editor'), {
    plugins: [Paragraph, ParagraphButtonUI],
    toolbar: ['paragraph']
  })
  .then(editor => {
    // Use paragraph commands
    editor.execute('paragraph');
    
    // Insert new paragraph at current selection
    const position = editor.model.document.selection.getFirstPosition();
    editor.execute('insertParagraph', { position });
  })
  .catch(error => {
    console.error(error);
  });

Architecture

The CKEditor 5 paragraph feature is built around several key components:

  • Paragraph Plugin: Core plugin that registers paragraph element in the model schema and sets up conversions
  • Command System: Two commands for paragraph operations - converting elements to paragraphs and inserting new paragraphs
  • UI Integration: Optional UI plugin that provides toolbar button for paragraph formatting
  • Schema Integration: Registers paragraph element that inherits from $block with proper conversion rules
  • Autoparagraphing: Automatic conversion of paragraph-like HTML elements to paragraphs during upcast

Capabilities

Paragraph Plugin

The main plugin that enables paragraph support in CKEditor 5 by registering the paragraph element schema, commands, and conversions.

/**
 * The paragraph feature for the editor. Introduces the `<paragraph>` element in the model
 * which renders as a `<p>` element in the DOM and data.
 */
class Paragraph extends Plugin {
  /** Plugin name identifier */
  static get pluginName(): 'Paragraph';
  /** Indicates this is an official CKEditor 5 plugin */
  static get isOfficialPlugin(): true;
  /** Set of HTML element names treated as paragraph-like for autoparagraphing */
  static paragraphLikeElements: Set<string>;
  /** Initializes the plugin, registers schema, commands, and conversions */
  init(): void;
}

The paragraphLikeElements set contains: 'blockquote', 'dd', 'div', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'p', 'td', 'th'.

Usage Example:

import { Paragraph } from "@ckeditor/ckeditor5-paragraph";

// Add to editor plugins
ClassicEditor.create(editorElement, {
  plugins: [Paragraph, /* other plugins */]
});

// Check if element is paragraph-like
const isParagraphLike = Paragraph.paragraphLikeElements.has('div'); // true

Paragraph Command

Command that converts all blocks in the model selection into paragraphs.

/**
 * The paragraph command that converts all blocks in the selection to paragraphs.
 */
class ParagraphCommand extends Command {
  /** Creates a new ParagraphCommand instance */
  constructor(editor: Editor);
  /** Indicates whether the selection start is placed in a paragraph */
  readonly value: boolean;
  /** Updates command state based on current selection */
  refresh(): void;
  /** 
   * Executes the command, converting selected blocks to paragraphs
   * @param options - Optional execution options
   * @param options.selection - The selection to apply command to (defaults to document selection)
   */
  execute(options?: { selection?: ModelSelection | ModelDocumentSelection }): void;
}

Usage Example:

// Execute paragraph command on current selection
editor.execute('paragraph');

// Execute on specific selection
const customSelection = editor.model.createSelection(range);
editor.execute('paragraph', { selection: customSelection });

// Check if current selection is in paragraph
const command = editor.commands.get('paragraph');
console.log(command.value); // true if selection is in paragraph

Insert Paragraph Command

Command that inserts a new paragraph at a specific document position.

/**
 * Command that inserts a new paragraph at a specific document position.
 * If paragraph is disallowed at the position, attempts to split ancestors to find valid location.
 */
class InsertParagraphCommand extends Command {
  /** Creates a new InsertParagraphCommand instance */
  constructor(editor: Editor);
  /**
   * Executes the command to insert a new paragraph
   * @param options - Execution options
   * @param options.position - Model position where to insert the paragraph
   * @param options.attributes - Optional attributes to set on the paragraph
   * @returns Position of the inserted paragraph or null if insertion failed
   */
  execute(options: {
    position: ModelPosition;
    attributes?: Record<string, unknown>;
  }): ModelPosition | null;
}

Usage Example:

import { ModelPosition } from "@ckeditor/ckeditor5-engine";

// Insert paragraph at current selection
const position = editor.model.document.selection.getFirstPosition();
const insertedPosition = editor.execute('insertParagraph', { position });

// Insert paragraph with attributes
const attributedPosition = editor.execute('insertParagraph', {
  position,
  attributes: { alignment: 'center' }
});

// Insert paragraph before an element
const element = editor.model.document.getRoot().getChild(0);
const beforePosition = editor.model.createPositionBefore(element);
editor.execute('insertParagraph', { position: beforePosition });

Paragraph Button UI

UI plugin that provides a paragraph button for the toolbar, allowing users to convert selected content to paragraphs.

/**
 * UI plugin that defines the 'paragraph' button for the toolbar.
 * Must be added manually to plugins - not loaded automatically by Paragraph plugin.
 */
class ParagraphButtonUI extends Plugin {
  /** Required plugins - depends on Paragraph plugin */
  static get requires(): readonly [typeof Paragraph];
  /** Initializes the UI plugin and registers the paragraph button component */
  init(): void;
}

Usage Example:

import { Paragraph, ParagraphButtonUI } from "@ckeditor/ckeditor5-paragraph";

ClassicEditor.create(editorElement, {
  plugins: [Paragraph, ParagraphButtonUI, /* other plugins */],
  toolbar: ['paragraph', 'heading1', 'heading2'] // Add paragraph button to toolbar
});

Type Definitions

// Command Registration Augmentation (from augmentation.ts)
declare module '@ckeditor/ckeditor5-core' {
  interface CommandsMap {
    insertParagraph: InsertParagraphCommand;
    paragraph: ParagraphCommand;
  }

  interface PluginsMap {
    'Paragraph': Paragraph;
  }
}

// CKEditor 5 Core Types (imported dependencies)
interface Editor {
  commands: CommandsMap;
  model: Model;
  execute(commandName: string, ...args: any[]): any;
}

interface Plugin {
  editor: Editor;
  static readonly pluginName?: string;
  static readonly requires?: readonly (typeof Plugin)[];
  static readonly isOfficialPlugin?: boolean;
  init?(): void;
}

interface Command {
  editor: Editor;
  value?: any;
  isEnabled: boolean;
  refresh(): void;
  execute(...args: any[]): any;
}

// CKEditor 5 Engine Types
interface ModelPosition {
  parent: ModelElement;
  offset: number;
  isAtStart: boolean;
  isAtEnd: boolean;
}

interface ModelSelection {
  getSelectedBlocks(): IterableIterator<ModelElement>;
  getFirstPosition(): ModelPosition | null;
}

interface ModelDocumentSelection extends ModelSelection {}

interface ModelElement {
  name: string;
  isEmpty: boolean;
  parent: ModelElement | ModelDocumentFragment;
  is(type: string, name?: string): boolean;
}

interface Model {
  document: ModelDocument;
  schema: ModelSchema;
  canEditAt(selection: ModelSelection | ModelPosition): boolean;
  change(callback: (writer: ModelWriter) => void): void;
  createPositionBefore(element: ModelElement): ModelPosition;
  createPositionAfter(element: ModelElement): ModelPosition;
  insertContent(content: ModelElement, position: ModelPosition): void;
}

interface ModelDocument {
  selection: ModelDocumentSelection;
}

interface ModelSchema {
  register(elementName: string, definition: any): void;
  checkChild(parent: ModelElement, child: string): boolean;
  findAllowedParent(position: ModelPosition, element: string): ModelElement | null;
  setAllowedAttributes(element: ModelElement, attributes: Record<string, unknown>, writer: ModelWriter): void;
  isObject(element: ModelElement): boolean;
}

interface ModelWriter {
  createElement(name: string): ModelElement;
  rename(element: ModelElement, newName: string): void;
  setSelection(element: ModelElement, position: 'in' | 'on'): void;
  createPositionAt(element: ModelElement, offset: number): ModelPosition;
  split(position: ModelPosition, limitElement: ModelElement): { position: ModelPosition };
}

Commands Registration

When the Paragraph plugin is loaded, it automatically registers these commands:

  • 'paragraph' - ParagraphCommand for converting blocks to paragraphs
  • 'insertParagraph' - InsertParagraphCommand for inserting new paragraphs

Schema Registration

The plugin registers a 'paragraph' element in the model schema with comprehensive conversion rules:

Model Schema Definition:

model.schema.register('paragraph', { inheritAllFrom: '$block' });

This registration:

  • Inherits from $block: Gets all block-level element properties (can contain text, inline elements)
  • Block-level element: Can be a root element, contains other elements, takes full width
  • Content model: Allows text content and inline elements like bold, italic, links

View Conversion:

editor.conversion.elementToElement({ model: 'paragraph', view: 'p' });
  • Downcast: Model <paragraph> elements render as HTML <p> elements
  • Upcast: HTML <p> elements convert to model <paragraph> elements

Autoparagraphing Conversion:

The plugin includes a special upcast converter for paragraph-like elements:

editor.conversion.for('upcast').elementToElement({
  model: (viewElement, { writer }) => {
    if (!Paragraph.paragraphLikeElements.has(viewElement.name)) {
      return null;
    }
    if (viewElement.isEmpty) {
      return null;
    }
    return writer.createElement('paragraph');
  },
  view: /.+/,
  converterPriority: 'low'
});

This ensures that paragraph-like HTML elements (div, h1-h6, li, td, etc.) are automatically converted to paragraphs when no other plugin handles them, maintaining content structure integrity.

Error Handling

The commands handle various error conditions gracefully:

ParagraphCommand Error Handling:

  • Non-editable positions: Command checks model.canEditAt() and returns early without executing
  • Schema validation: Only converts blocks that pass checkCanBecomeParagraph() validation
  • Object elements: Cannot convert schema objects (like images, tables) to paragraphs

Example:

// Command will safely handle non-editable selections
editor.model.change(writer => {
  const readOnlySelection = writer.createSelection(readOnlyElement, 'in');
  editor.execute('paragraph', { selection: readOnlySelection }); // Safely returns without error
});

InsertParagraphCommand Error Handling:

  • Non-editable positions: Returns null if position is not editable
  • Schema constraints: Returns null if no valid position can be found for paragraph insertion
  • Ancestor splitting: Attempts to split ancestors to find valid insertion point before failing

Example:

// Command handles invalid positions gracefully
const invalidPosition = editor.model.createPositionAt(objectElement, 0);
const result = editor.execute('insertParagraph', { position: invalidPosition });
if (result === null) {
  console.log('Could not insert paragraph at this position');
}

Plugin Loading Errors:

  • Missing dependencies: ParagraphButtonUI will throw if Paragraph plugin is not loaded
  • Plugin conflicts: Schema conflicts resolved through CKEditor 5's plugin priority system

Integration Notes

  • The Paragraph plugin is foundational and typically loaded in most CKEditor 5 configurations
  • ParagraphButtonUI must be added manually - it's not loaded automatically
  • The paragraph element serves as the default block element for content that doesn't match other registered elements
  • Autoparagraphing ensures content structure integrity when pasting or loading HTML content