CKEditor 5 Word Count is a plugin that provides real-time word and character counting functionality for CKEditor 5. It calculates text statistics by converting editor model data to plain text and supports configurable display options, custom update callbacks, and seamless integration with the CKEditor 5 architecture.
npm install ckeditor5 (part of the main CKEditor 5 package)import { WordCount } from "@ckeditor/ckeditor5-word-count";For accessing configuration types:
import type { WordCountConfig } from "@ckeditor/ckeditor5-word-count";import { ClassicEditor } from "@ckeditor/ckeditor5-editor-classic";
import { WordCount } from "@ckeditor/ckeditor5-word-count";
// Basic plugin usage
ClassicEditor
.create(document.querySelector('#editor'), {
plugins: [WordCount],
wordCount: {
displayWords: true,
displayCharacters: true,
onUpdate: (stats) => {
console.log(`Words: ${stats.words}, Characters: ${stats.characters}`);
}
}
})
.then(editor => {
const wordCountPlugin = editor.plugins.get('WordCount');
// Access current counts
console.log('Current words:', wordCountPlugin.words);
console.log('Current characters:', wordCountPlugin.characters);
// Get the display container
const container = wordCountPlugin.wordCountContainer;
document.body.appendChild(container);
});The Word Count plugin is built around several key components:
The main WordCount plugin class that provides word and character counting functionality.
class WordCount extends Plugin {
/** The number of characters in the editor (observable, readonly, computed via getter) */
readonly characters: number;
/** The number of words in the editor (observable, readonly, computed via getter) */
readonly words: number;
/** Creates a self-updating HTML element for displaying counts */
readonly wordCountContainer: HTMLElement;
/** Plugin name identifier */
static readonly pluginName: 'WordCount';
/** Indicates this is an official CKEditor 5 plugin */
static readonly isOfficialPlugin: true;
/**
* Creates a new WordCount plugin instance
* @param editor - The editor instance this plugin will be attached to
*/
constructor(editor: Editor);
/** Initialize the plugin functionality */
init(): void;
/** Clean up plugin resources */
destroy(): void;
}Usage Examples:
// Access plugin instance
const wordCountPlugin = editor.plugins.get('WordCount');
// Get current statistics - properties are computed on access via getters
const currentWords = wordCountPlugin.words;
const currentCharacters = wordCountPlugin.characters;
// Get display container and add to DOM
const displayContainer = wordCountPlugin.wordCountContainer;
document.getElementById('word-count-area').appendChild(displayContainer);
// Listen for updates
wordCountPlugin.on('update', (evt, data) => {
console.log(`Updated: ${data.words} words, ${data.characters} characters`);
});Configuration interface for customizing word count behavior and display.
interface WordCountConfig {
/** Show/hide word counter in display container (default: true) */
displayWords?: boolean;
/** Show/hide character counter in display container (default: true) */
displayCharacters?: boolean;
/** Callback function executed when counts are updated */
onUpdate?: (data: { words: number; characters: number }) => void;
/** HTML element to automatically append the word count container to */
container?: HTMLElement;
}Usage Examples:
// Hide word counter, show only characters
const config: WordCountConfig = {
displayWords: false,
displayCharacters: true
};
// Custom update handler
const config: WordCountConfig = {
onUpdate: (stats) => {
// Update external UI
document.getElementById('word-display').textContent = `${stats.words} words`;
document.getElementById('char-display').textContent = `${stats.characters} characters`;
// Validate against limits
if (stats.words > 1000) {
console.warn('Word limit exceeded');
}
}
};
// Auto-append to specific container
const config: WordCountConfig = {
container: document.getElementById('editor-stats')
};Event type fired when word and character counts are updated.
type WordCountUpdateEvent = {
name: 'update';
args: [{ words: number; characters: number }];
};Usage Example:
// Listen for count updates
wordCountPlugin.on('update', (evt, data) => {
console.log(`Words: ${data.words}, Characters: ${data.characters}`);
// Update progress bars, validation, etc.
updateProgressIndicator(data.words, 500); // 500 word limit
});
// Note: The plugin also maintains private observable properties _wordsLabel and _charactersLabel
// that are automatically updated and used to display localized text in the wordCountContainerInternal utility function for converting model elements to plain text (exported for advanced use cases).
/**
* Returns a plain text representation of an element and its children
* @param item - Model item to convert to plain text
* @returns Plain text representing the model's data
*/
function modelElementToPlainText(item: ModelItem): string;Note: This function is exported as modelElementToPlainText as _modelElementToPlainText from the main index, indicating it's intended for internal use but available for advanced use cases where custom text processing is needed.
// Core model types from CKEditor 5 engine
interface ModelItem {
is(type: string): boolean;
}
interface ModelElement extends ModelItem {
getChildren(): Iterable<ModelItem>;
}
// Editor types from CKEditor 5 core
interface Editor {
model: {
document: {
getRoots(): Iterable<ModelElement>;
on(event: string, callback: Function): void;
};
};
config: {
get(path: string): any;
};
plugins: {
get(name: string): Plugin;
};
t(message: string, ...values: any[]): string;
}
interface Plugin {
constructor(editor: Editor);
init?(): void;
destroy?(): void;
static pluginName?: string;
static isOfficialPlugin?: boolean;
}The plugin handles several common scenarios:
Common Error Scenarios:
// Plugin not loaded
try {
const wordCount = editor.plugins.get('WordCount');
} catch (error) {
console.error('WordCount plugin not available:', error);
}
// Invalid container element
const config: WordCountConfig = {
container: document.getElementById('nonexistent') // Will be ignored if null
};
// Cleanup on editor destruction
editor.destroy().then(() => {
// WordCount plugin automatically cleaned up
console.log('Editor and plugins destroyed');
});