CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tiptap--extension-character-count

Character count extension for Tiptap rich text editor with configurable limits and automatic content trimming

Pending
Overview
Eval results
Files

index.mddocs/

Tiptap Character Count Extension

Character count extension for Tiptap rich text editor with configurable limits and automatic content trimming. This extension provides real-time character and word counting functionality with optional character limits, automatic content enforcement, and support for different counting modes.

Package Information

  • Package Name: @tiptap/extension-character-count
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @tiptap/extension-character-count

Core Imports

import { CharacterCount } from "@tiptap/extension-character-count";
import type { CharacterCountOptions } from "@tiptap/extension-character-count";

For default import:

import CharacterCount from "@tiptap/extension-character-count";

For CommonJS:

const { CharacterCount } = require("@tiptap/extension-character-count");

Basic Usage

import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

// Basic usage with default options
const editor = new Editor({
  extensions: [CharacterCount],
  content: "<p>Hello world!</p>",
});

// With character limit
const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      limit: 280,
    })
  ],
  content: "<p>Hello world!</p>",
});

// Access character count
const characterCount = editor.storage.characterCount.characters();
const wordCount = editor.storage.characterCount.words();

console.log(`Characters: ${characterCount}, Words: ${wordCount}`);

Architecture

The extension is built around several key components:

  • Extension Core: Built on Tiptap's Extension framework, integrating with ProseMirror's plugin system
  • Character Counting: Supports both text-based (textSize) and node-based (nodeSize) counting modes
  • Limit Enforcement: Real-time transaction filtering prevents exceeding character limits
  • Content Trimming: Automatic content trimming for pasted content and initialization
  • Storage API: Provides runtime access to character and word counts through editor storage

Capabilities

Character Count Extension

The main extension providing character counting functionality with optional limits and automatic enforcement.

const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;

Configuration Options

Configuration interface for customizing the character count behavior.

interface CharacterCountOptions {
  /**
   * The maximum number of characters that should be allowed. Defaults to null.
   * @default null
   * @example 180
   */
  limit: number | null | undefined;
  
  /**
   * The mode by which the size is calculated. If set to 'textSize', the textContent of the document is used.
   * If set to 'nodeSize', the nodeSize of the document is used.
   * @default 'textSize'
   * @example 'textSize'
   */
  mode: 'textSize' | 'nodeSize';
  
  /**
   * The text counter function to use. Defaults to a simple character count.
   * @default (text) => text.length
   * @example (text) => [...new Intl.Segmenter().segment(text)].length
   */
  textCounter: (text: string) => number;
  
  /**
   * The word counter function to use. Defaults to a simple word count.
   * @default (text) => text.split(' ').filter(word => word !== '').length
   * @example (text) => text.split(/\s+/).filter(word => word !== '').length
   */
  wordCounter: (text: string) => number;
}

Storage Interface

Runtime interface for accessing character and word counts.

interface CharacterCountStorage {
  /**
   * Get the number of characters for the current document.
   * @param options The options for the character count. (optional)
   * @param options.node The node to get the characters from. Defaults to the current document.
   * @param options.mode The mode by which the size is calculated. If set to 'textSize', the textContent of the document is used.
   */
  characters: (options?: { 
    node?: ProseMirrorNode; 
    mode?: 'textSize' | 'nodeSize' 
  }) => number;

  /**
   * Get the number of words for the current document.
   * @param options The options for the character count. (optional)
   * @param options.node The node to get the words from. Defaults to the current document.
   */
  words: (options?: { node?: ProseMirrorNode }) => number;
}

ProseMirror Node Type

Type definition for ProseMirror nodes used in the storage interface.

// Imported from @tiptap/pm/model
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';

Advanced Usage Examples

Custom Character Counting

import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

// Using Unicode-aware character counting
const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      limit: 280,
      textCounter: (text) => {
        // Count grapheme clusters instead of code units
        return [...new Intl.Segmenter().segment(text)].length;
      },
    })
  ],
});

Custom Word Counting

import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

// Using custom word counting logic
const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      wordCounter: (text) => {
        // Split on any whitespace and filter empty strings
        return text.split(/\s+/).filter(word => word !== '').length;
      },
    })
  ],
});

Node Size Mode

import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

// Use nodeSize instead of textContent for counting
const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      mode: 'nodeSize',
      limit: 1000,
    })
  ],
});

Runtime Character Count Access

import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

const editor = new Editor({
  extensions: [CharacterCount],
});

// Get counts for current document
const chars = editor.storage.characterCount.characters();
const words = editor.storage.characterCount.words();

// Get counts for specific node with custom mode
const nodeChars = editor.storage.characterCount.characters({
  node: someSpecificNode,
  mode: 'nodeSize'
});

// Get word count for specific node
const nodeWords = editor.storage.characterCount.words({
  node: someSpecificNode
});

Limit Enforcement Behavior

The extension automatically enforces character limits through ProseMirror's transaction filtering:

  • Normal typing: Prevents typing when at the character limit
  • Pasted content: Automatically trims pasted content to fit within the limit
  • Initial content: Trims initial content that exceeds the limit on editor initialization
  • Complex nodes: Handles cases where trimming within complex nodes (like tables) might still exceed limits
import { CharacterCount } from "@tiptap/extension-character-count";
import { Editor } from "@tiptap/core";

const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      limit: 100, // Strict 100 character limit
    })
  ],
  content: "<p>This content will be automatically trimmed if it exceeds the 100 character limit during initialization</p>",
});

// The editor will automatically prevent further typing at the limit
// and trim any pasted content that would exceed the limit

Error Handling

The extension includes built-in error handling and warnings:

  • Initial content trimming: Console warning when initial content is automatically trimmed
  • Transaction blocking: Silent prevention of transactions that would exceed limits
  • Paste trimming: Automatic handling of oversized pasted content with fallback blocking

No exceptions are thrown during normal operation, making the extension safe to use in production environments.

Install with Tessl CLI

npx tessl i tessl/npm-tiptap--extension-character-count

docs

index.md

tile.json