Split view
Show tracked changes side by side, with deletions in the left pane and additions in the right pane. Both panes are editable and keep matching blocks aligned.
Install
First, contact our team to purchase the Compare add-on and add it to your plan.
After gaining access, configure your package manager by following the private registry guide.
Then, install the package:
npm install @tiptap-pro/extension-split-view @tiptap-pro/extension-tracked-changesConnect the editors
Your main editor is the source of truth for editor content. Create two additional editors to show the left and right pane of the split view. Add the TrackedChanges extension to all three editors, and add the SplitView to the two editors that correspond to the panes.
import { Editor } from '@tiptap/core'
import { TrackedChanges } from '@tiptap-pro/extension-tracked-changes'
import { SplitView, createSplitView } from '@tiptap-pro/extension-split-view'
const mainEditor = new Editor({
extensions: [
// ... other extensions
TrackedChanges,
],
content: '<p>Start writing here.</p>',
})
const beforeEditor = new Editor({
extensions: [
// ... other extensions
TrackedChanges,
SplitView,
],
})
const afterEditor = new Editor({
extensions: [
// ... other extensions
TrackedChanges,
SplitView,
],
})
let manager = createSplitView({ mainEditor, beforeEditor, afterEditor })The content is managed in the main editor. createSplitView() connects the panes to the main editor. Edits and accept/reject actions synchronize across all three editors.
Arrange the panes
Hide the main editor while split view is open. Give both panes the same top padding and use a shared scrolling container.
<div id="main" hidden></div>
<div id="split">
<div id="before"></div>
<div id="after"></div>
</div>#split {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
overflow: auto;
}
#split[hidden] {
display: none;
}Gray spacers fill height differences or missing blocks. Both spacers use the larger of the two gaps to the next block, measured from your CSS, as their bottom margin. Zero-height spacers add spacing without showing a gray box. Spacers are never saved in the document.
Switch views
Destroy the manager to stop synchronization and remove pane filtering. The editors stay alive.
manager.destroy()
document.querySelector('#split')?.setAttribute('hidden', '')
document.querySelector('#main')?.removeAttribute('hidden')To return to split view, reconnect. Both panes receive the main editor's current document.
document.querySelector('#main')?.setAttribute('hidden', '')
document.querySelector('#split')?.removeAttribute('hidden')
manager = createSplitView({ mainEditor, beforeEditor, afterEditor })Destroy all three editors when your component unmounts. Each editor has its own undo history; split view does not provide shared undo.
Use with the Pages extension
To compare paginated documents, add the Pages extension to both split-view panes with matching page formats, margins, headers, footers, and zoom. Split View detects Pages automatically and keeps blocks aligned across page boundaries. Use a shared horizontally scrollable container.
Separate product
The Pages extension is a different product than Split View.
API reference
SplitView
Install in both panes alongside TrackedChanges. No configuration is needed. It stays inactive until createSplitView() connects the editors.
createSplitView(options): SplitViewManager
Returns a SplitViewManager. Pass three distinct, mounted editors with identical schemas. An editor can belong to only one active manager.
| Option | Type | Description |
|---|---|---|
mainEditor | Editor | Supplies the initial document. |
beforeEditor | Editor | Shows deletions and unchanged content. |
afterEditor | Editor | Shows additions and unchanged content. |
createSpacer | (options: SplitViewSpacerOptions) => HTMLElement | Optional custom spacer element. |
Other suggestion types, such as formatting changes, remain visible in both panes. All three editors retain the full document, including content hidden in a pane. Destroy an existing manager before connecting the same editors again.
Manager methods
| Method | Returns | Description |
|---|---|---|
getMainEditor() | Editor | undefined | Returns the main editor. |
getBeforeEditor() | Editor | undefined | Returns the Before pane. |
getAfterEditor() | Editor | undefined | Returns the After pane. |
update() | void | Schedules alignment after a custom layout or CSS change. |
destroy() | void | Stops synchronization, removes spacers and filtering, and releases resources. Safe to call twice. |
Getters return undefined after destruction. Destroying any connected editor also destroys the manager. Content changes, resizing, image loads, and font loading update alignment automatically.
Style spacers
The default spacer is a div with class tiptap-split-view-spacer. Set its background with CSS:
#split {
--tiptap-split-view-spacer-background: #eee;
}For a custom element, supply createSpacer:
manager.destroy()
manager = createSplitView({
mainEditor,
beforeEditor,
afterEditor,
createSpacer({ document }) {
const spacer = document.createElement('div')
spacer.style.background = '#eee'
return spacer
},
})SplitViewSpacerOptions contains document: Document and side: 'original' | 'modified'. The createSpacer callback should return an HTMLElement; the manager controls its height, margins, and visibility. Custom elements do not receive the default class automatically.