or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@tiptap/extension-character-count@3.4.x

To install, run

npx @tessl/cli install tessl/npm-tiptap--extension-character-count@3.4.0

0

# 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.