or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-editor-api.mdeditor-management.mdindex.mdmenu-components.mdvue-components.mdvue-renderers.md

vue-renderers.mddocs/

0

# Vue Renderers

1

2

Utilities for creating custom Vue-based node and mark views, enabling full Vue component integration within editor content.

3

4

## Capabilities

5

6

### VueRenderer Class

7

8

Utility class for rendering Vue components inside the editor with full component lifecycle support.

9

10

```typescript { .api }

11

/**

12

* Renders Vue components inside the editor

13

* Handles component lifecycle and Vue context integration

14

*/

15

class VueRenderer {

16

constructor(component: Component, options: VueRendererOptions);

17

18

/** The editor instance */

19

editor: Editor;

20

21

/** The Vue component to render */

22

component: Component;

23

24

/** The DOM element container */

25

el: Element | null;

26

27

/** Reactive props object */

28

props: Record<string, any>;

29

30

/** Get the rendered DOM element */

31

get element(): Element | null;

32

33

/** Get the component reference (exposed API or proxy) */

34

get ref(): any;

35

36

/**

37

* Update component props reactively

38

* @param props - New props to merge

39

*/

40

updateProps(props?: Record<string, any>): void;

41

42

/**

43

* Render the component and return rendered details

44

* @private Internal method for component rendering

45

*/

46

renderComponent(): {

47

vNode: ReturnType<typeof h> | null;

48

destroy: () => void;

49

el: Element | null;

50

};

51

52

/** Destroy the renderer and cleanup */

53

destroy(): void;

54

}

55

56

interface VueRendererOptions {

57

/** Editor instance */

58

editor: Editor;

59

/** Initial props for the component */

60

props?: Record<string, any>;

61

}

62

```

63

64

**Key Features:**

65

- Full Vue component lifecycle support

66

- Reactive props updates

67

- Vue app context integration

68

- Support for both Composition and Options API

69

- Automatic cleanup and memory management

70

71

**Usage Examples:**

72

73

```typescript

74

import { VueRenderer } from "@tiptap/vue-3";

75

import MyComponent from "./MyComponent.vue";

76

77

// Create a renderer

78

const renderer = new VueRenderer(MyComponent, {

79

editor: editorInstance,

80

props: {

81

title: "Hello World",

82

count: 0,

83

},

84

});

85

86

// Access the DOM element

87

const element = renderer.element;

88

89

// Access component methods/data

90

const componentRef = renderer.ref;

91

componentRef.someMethod();

92

93

// Update props

94

renderer.updateProps({ count: 1 });

95

96

// Cleanup

97

renderer.destroy();

98

```

99

100

### VueNodeViewRenderer Function

101

102

Creates node view renderers for Vue components, allowing custom node types to be rendered as Vue components.

103

104

```typescript { .api }

105

/**

106

* Creates a node view renderer for Vue components

107

* @param component - Vue component to render

108

* @param options - Optional configuration

109

* @returns NodeViewRenderer function

110

*/

111

function VueNodeViewRenderer(

112

component: Component,

113

options?: VueNodeViewRendererOptions

114

): NodeViewRenderer;

115

116

interface VueNodeViewRendererOptions extends NodeViewRendererOptions {

117

/**

118

* Custom update function to control when node view updates

119

* @param props - Update parameters

120

* @returns Whether to update the node view

121

*/

122

update?: ((props: {

123

oldNode: ProseMirrorNode;

124

oldDecorations: readonly Decoration[];

125

oldInnerDecorations: DecorationSource;

126

newNode: ProseMirrorNode;

127

newDecorations: readonly Decoration[];

128

innerDecorations: DecorationSource;

129

updateProps: () => void;

130

}) => boolean) | null;

131

}

132

133

const nodeViewProps: {

134

editor: { type: PropType<Editor>; required: true };

135

node: { type: PropType<ProseMirrorNode>; required: true };

136

decorations: { type: PropType<DecorationWithType[]>; required: true };

137

selected: { type: PropType<boolean>; required: true };

138

extension: { type: PropType<Node>; required: true };

139

getPos: { type: PropType<() => number | undefined>; required: true };

140

updateAttributes: { type: PropType<(attributes: Record<string, any>) => void>; required: true };

141

deleteNode: { type: PropType<() => void>; required: true };

142

view: { type: PropType<EditorView>; required: true };

143

innerDecorations: { type: PropType<DecorationSource>; required: true };

144

HTMLAttributes: { type: PropType<Record<string, any>>; required: true };

145

};

146

```

147

148

**Usage Examples:**

149

150

```typescript

151

// Define a custom node extension

152

import { Node } from "@tiptap/core";

153

import { VueNodeViewRenderer } from "@tiptap/vue-3";

154

import CustomNodeComponent from "./CustomNodeComponent.vue";

155

156

const CustomNode = Node.create({

157

name: 'customNode',

158

159

group: 'block',

160

161

content: 'inline*',

162

163

parseHTML() {

164

return [{ tag: 'div[data-type="custom-node"]' }];

165

},

166

167

renderHTML({ HTMLAttributes }) {

168

return ['div', { 'data-type': 'custom-node', ...HTMLAttributes }, 0];

169

},

170

171

addNodeView() {

172

return VueNodeViewRenderer(CustomNodeComponent);

173

},

174

});

175

176

// Use in editor

177

const editor = useEditor({

178

extensions: [StarterKit, CustomNode],

179

content: '<p>Hello world</p>',

180

});

181

```

182

183

**Custom Node Component:**

184

185

```typescript

186

<!-- CustomNodeComponent.vue -->

187

<template>

188

<NodeViewWrapper class="custom-node">

189

<div class="node-header">

190

<button @click="deleteNode">Delete</button>

191

<span>{{ node.attrs.title || 'Untitled' }}</span>

192

</div>

193

<NodeViewContent class="content" />

194

</NodeViewWrapper>

195

</template>

196

197

<script setup>

198

import { NodeViewContent, NodeViewWrapper, nodeViewProps } from "@tiptap/vue-3";

199

200

const props = defineProps(nodeViewProps);

201

202

const deleteNode = () => {

203

props.deleteNode();

204

};

205

</script>

206

```

207

208

### VueMarkViewRenderer Function

209

210

Creates mark view renderers for Vue components, allowing custom mark types to be rendered as Vue components.

211

212

```typescript { .api }

213

/**

214

* Creates a mark view renderer for Vue components

215

* @param component - Vue component to render

216

* @param options - Optional configuration

217

* @returns MarkViewRenderer function

218

*/

219

function VueMarkViewRenderer(

220

component: Component,

221

options?: VueMarkViewRendererOptions

222

): MarkViewRenderer;

223

224

interface VueMarkViewRendererOptions extends MarkViewRendererOptions {

225

/** HTML tag to render as */

226

as?: string;

227

/** CSS class name */

228

className?: string;

229

/** HTML attributes */

230

attrs?: { [key: string]: string };

231

}

232

233

const markViewProps: {

234

editor: { type: PropType<Editor>; required: true };

235

mark: { type: PropType<Mark>; required: true };

236

extension: { type: PropType<Mark>; required: true };

237

inline: { type: PropType<boolean>; required: true };

238

view: { type: PropType<EditorView>; required: true };

239

updateAttributes: { type: PropType<(attributes: Record<string, any>) => void>; required: true };

240

HTMLAttributes: { type: PropType<Record<string, any>>; required: true };

241

};

242

243

/**

244

* Component for rendering mark view content

245

*/

246

const MarkViewContent: DefineComponent<{

247

as?: {

248

type: PropType<string>;

249

default: 'span';

250

};

251

}>;

252

```

253

254

**Usage Examples:**

255

256

```typescript

257

// Define a custom mark extension

258

import { Mark } from "@tiptap/core";

259

import { VueMarkViewRenderer } from "@tiptap/vue-3";

260

import CustomMarkComponent from "./CustomMarkComponent.vue";

261

262

const CustomMark = Mark.create({

263

name: 'customMark',

264

265

parseHTML() {

266

return [{ tag: 'span[data-type="custom-mark"]' }];

267

},

268

269

renderHTML({ HTMLAttributes }) {

270

return ['span', { 'data-type': 'custom-mark', ...HTMLAttributes }, 0];

271

},

272

273

addMarkView() {

274

return VueMarkViewRenderer(CustomMarkComponent);

275

},

276

});

277

```

278

279

**Custom Mark Component:**

280

281

```typescript

282

<!-- CustomMarkComponent.vue -->

283

<template>

284

<span class="custom-mark" :style="markStyle">

285

<MarkViewContent />

286

<button @click="removeMark" class="remove-btn">×</button>

287

</span>

288

</template>

289

290

<script setup>

291

import { MarkViewContent, markViewProps } from "@tiptap/vue-3";

292

import { computed } from 'vue';

293

294

const props = defineProps(markViewProps);

295

296

const markStyle = computed(() => ({

297

backgroundColor: props.mark.attrs.color || '#ffeb3b',

298

padding: '2px 4px',

299

borderRadius: '2px',

300

}));

301

302

const removeMark = () => {

303

props.updateAttributes(null); // Remove the mark

304

};

305

</script>

306

```

307

308

### Advanced Renderer Patterns

309

310

**Node View with State Management:**

311

312

```typescript

313

<!-- StatefulNodeComponent.vue -->

314

<template>

315

<NodeViewWrapper>

316

<div class="stateful-node">

317

<div class="controls">

318

<button @click="increment">Count: {{ count }}</button>

319

<button @click="updateAttributes({ persistent: count })">Save</button>

320

</div>

321

<NodeViewContent />

322

</div>

323

</NodeViewWrapper>

324

</template>

325

326

<script setup>

327

import { ref, onMounted } from 'vue';

328

import { NodeViewWrapper, NodeViewContent, nodeViewProps } from "@tiptap/vue-3";

329

330

const props = defineProps(nodeViewProps);

331

332

const count = ref(props.node.attrs.persistent || 0);

333

334

const increment = () => {

335

count.value++;

336

};

337

338

onMounted(() => {

339

// Component lifecycle works normally

340

console.log('Node view mounted');

341

});

342

</script>

343

```

344

345

**Custom Update Logic:**

346

347

```typescript

348

import { VueNodeViewRenderer } from "@tiptap/vue-3";

349

350

const CustomNode = Node.create({

351

addNodeView() {

352

return VueNodeViewRenderer(CustomNodeComponent, {

353

update: ({ oldNode, newNode, updateProps }) => {

354

// Only update if specific attributes changed

355

if (oldNode.attrs.title !== newNode.attrs.title) {

356

updateProps();

357

return true;

358

}

359

return false;

360

},

361

});

362

},

363

});

364

```

365

366

## Types

367

368

```typescript { .api }

369

interface NodeViewProps {

370

editor: Editor;

371

node: ProseMirrorNode;

372

decorations: DecorationWithType[];

373

selected: boolean;

374

extension: Node;

375

getPos: () => number | undefined;

376

updateAttributes: (attributes: Record<string, any>) => void;

377

deleteNode: () => void;

378

view: EditorView;

379

innerDecorations: DecorationSource;

380

HTMLAttributes: Record<string, any>;

381

}

382

383

interface MarkViewProps {

384

editor: Editor;

385

mark: ProseMirrorMark;

386

extension: Mark;

387

inline: boolean;

388

view: EditorView;

389

updateAttributes: (attributes: Record<string, any>) => void;

390

HTMLAttributes: Record<string, any>;

391

}

392

393

type Component = DefineComponent | ComponentOptions;

394

type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView;

395

type MarkViewRenderer = (props: MarkViewRendererProps) => MarkView;

396

```