Character count extension for Tiptap rich text editor with configurable limits and automatic content trimming
npx @tessl/cli install tessl/npm-tiptap--extension-character-count@3.4.00
# Tiptap Character Count Extension
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: @tiptap/extension-character-count
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @tiptap/extension-character-count`
10
11
## Core Imports
12
13
```typescript
14
import { CharacterCount } from "@tiptap/extension-character-count";
15
import type { CharacterCountOptions } from "@tiptap/extension-character-count";
16
```
17
18
For default import:
19
20
```typescript
21
import CharacterCount from "@tiptap/extension-character-count";
22
```
23
24
For CommonJS:
25
26
```javascript
27
const { CharacterCount } = require("@tiptap/extension-character-count");
28
```
29
30
## Basic Usage
31
32
```typescript
33
import { CharacterCount } from "@tiptap/extension-character-count";
34
import { Editor } from "@tiptap/core";
35
36
// Basic usage with default options
37
const editor = new Editor({
38
extensions: [CharacterCount],
39
content: "<p>Hello world!</p>",
40
});
41
42
// With character limit
43
const editor = new Editor({
44
extensions: [
45
CharacterCount.configure({
46
limit: 280,
47
})
48
],
49
content: "<p>Hello world!</p>",
50
});
51
52
// Access character count
53
const characterCount = editor.storage.characterCount.characters();
54
const wordCount = editor.storage.characterCount.words();
55
56
console.log(`Characters: ${characterCount}, Words: ${wordCount}`);
57
```
58
59
## Architecture
60
61
The extension is built around several key components:
62
63
- **Extension Core**: Built on Tiptap's Extension framework, integrating with ProseMirror's plugin system
64
- **Character Counting**: Supports both text-based (`textSize`) and node-based (`nodeSize`) counting modes
65
- **Limit Enforcement**: Real-time transaction filtering prevents exceeding character limits
66
- **Content Trimming**: Automatic content trimming for pasted content and initialization
67
- **Storage API**: Provides runtime access to character and word counts through editor storage
68
69
## Capabilities
70
71
### Character Count Extension
72
73
The main extension providing character counting functionality with optional limits and automatic enforcement.
74
75
```typescript { .api }
76
const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;
77
```
78
79
### Configuration Options
80
81
Configuration interface for customizing the character count behavior.
82
83
```typescript { .api }
84
interface CharacterCountOptions {
85
/**
86
* The maximum number of characters that should be allowed. Defaults to null.
87
* @default null
88
* @example 180
89
*/
90
limit: number | null | undefined;
91
92
/**
93
* The mode by which the size is calculated. If set to 'textSize', the textContent of the document is used.
94
* If set to 'nodeSize', the nodeSize of the document is used.
95
* @default 'textSize'
96
* @example 'textSize'
97
*/
98
mode: 'textSize' | 'nodeSize';
99
100
/**
101
* The text counter function to use. Defaults to a simple character count.
102
* @default (text) => text.length
103
* @example (text) => [...new Intl.Segmenter().segment(text)].length
104
*/
105
textCounter: (text: string) => number;
106
107
/**
108
* The word counter function to use. Defaults to a simple word count.
109
* @default (text) => text.split(' ').filter(word => word !== '').length
110
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
111
*/
112
wordCounter: (text: string) => number;
113
}
114
```
115
116
### Storage Interface
117
118
Runtime interface for accessing character and word counts.
119
120
```typescript { .api }
121
interface CharacterCountStorage {
122
/**
123
* Get the number of characters for the current document.
124
* @param options The options for the character count. (optional)
125
* @param options.node The node to get the characters from. Defaults to the current document.
126
* @param options.mode The mode by which the size is calculated. If set to 'textSize', the textContent of the document is used.
127
*/
128
characters: (options?: {
129
node?: ProseMirrorNode;
130
mode?: 'textSize' | 'nodeSize'
131
}) => number;
132
133
/**
134
* Get the number of words for the current document.
135
* @param options The options for the character count. (optional)
136
* @param options.node The node to get the words from. Defaults to the current document.
137
*/
138
words: (options?: { node?: ProseMirrorNode }) => number;
139
}
140
```
141
142
### ProseMirror Node Type
143
144
Type definition for ProseMirror nodes used in the storage interface.
145
146
```typescript { .api }
147
// Imported from @tiptap/pm/model
148
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
149
```
150
151
## Advanced Usage Examples
152
153
### Custom Character Counting
154
155
```typescript
156
import { CharacterCount } from "@tiptap/extension-character-count";
157
import { Editor } from "@tiptap/core";
158
159
// Using Unicode-aware character counting
160
const editor = new Editor({
161
extensions: [
162
CharacterCount.configure({
163
limit: 280,
164
textCounter: (text) => {
165
// Count grapheme clusters instead of code units
166
return [...new Intl.Segmenter().segment(text)].length;
167
},
168
})
169
],
170
});
171
```
172
173
### Custom Word Counting
174
175
```typescript
176
import { CharacterCount } from "@tiptap/extension-character-count";
177
import { Editor } from "@tiptap/core";
178
179
// Using custom word counting logic
180
const editor = new Editor({
181
extensions: [
182
CharacterCount.configure({
183
wordCounter: (text) => {
184
// Split on any whitespace and filter empty strings
185
return text.split(/\s+/).filter(word => word !== '').length;
186
},
187
})
188
],
189
});
190
```
191
192
### Node Size Mode
193
194
```typescript
195
import { CharacterCount } from "@tiptap/extension-character-count";
196
import { Editor } from "@tiptap/core";
197
198
// Use nodeSize instead of textContent for counting
199
const editor = new Editor({
200
extensions: [
201
CharacterCount.configure({
202
mode: 'nodeSize',
203
limit: 1000,
204
})
205
],
206
});
207
```
208
209
### Runtime Character Count Access
210
211
```typescript
212
import { CharacterCount } from "@tiptap/extension-character-count";
213
import { Editor } from "@tiptap/core";
214
215
const editor = new Editor({
216
extensions: [CharacterCount],
217
});
218
219
// Get counts for current document
220
const chars = editor.storage.characterCount.characters();
221
const words = editor.storage.characterCount.words();
222
223
// Get counts for specific node with custom mode
224
const nodeChars = editor.storage.characterCount.characters({
225
node: someSpecificNode,
226
mode: 'nodeSize'
227
});
228
229
// Get word count for specific node
230
const nodeWords = editor.storage.characterCount.words({
231
node: someSpecificNode
232
});
233
```
234
235
### Limit Enforcement Behavior
236
237
The extension automatically enforces character limits through ProseMirror's transaction filtering:
238
239
- **Normal typing**: Prevents typing when at the character limit
240
- **Pasted content**: Automatically trims pasted content to fit within the limit
241
- **Initial content**: Trims initial content that exceeds the limit on editor initialization
242
- **Complex nodes**: Handles cases where trimming within complex nodes (like tables) might still exceed limits
243
244
```typescript
245
import { CharacterCount } from "@tiptap/extension-character-count";
246
import { Editor } from "@tiptap/core";
247
248
const editor = new Editor({
249
extensions: [
250
CharacterCount.configure({
251
limit: 100, // Strict 100 character limit
252
})
253
],
254
content: "<p>This content will be automatically trimmed if it exceeds the 100 character limit during initialization</p>",
255
});
256
257
// The editor will automatically prevent further typing at the limit
258
// and trim any pasted content that would exceed the limit
259
```
260
261
## Error Handling
262
263
The extension includes built-in error handling and warnings:
264
265
- **Initial content trimming**: Console warning when initial content is automatically trimmed
266
- **Transaction blocking**: Silent prevention of transactions that would exceed limits
267
- **Paste trimming**: Automatic handling of oversized pasted content with fallback blocking
268
269
No exceptions are thrown during normal operation, making the extension safe to use in production environments.