Rapid, repeated dragging caused the page to freeze.
Author: DongqiangHeCreated Sep 15, 2026Updated Sep 15, 2026
Labelsstatus: needs-reproductionstatus: triagearea: editorarea: coreimpact: medium
Affected Packages
core
Tiptap Version
3.31.3
Browser Used
Chrome
What happened?
Rapid, repeated dragging caused the page to freeze.
Test code is very simple
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TipTap Markdown Editor</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #f7f9fa;
color: #333;
display: flex;
justify-content: center;
padding: 2rem;
}
.editor-container {
width: 100%;
max-width: 900px;
background: #fff;
border: 1px solid #e1e4e8;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Toolbar Styles */
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
padding: 8px;
background: #fafbfc;
border-bottom: 1px solid #e1e4e8;
}
.toolbar button {
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
padding: 6px 10px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
color: #444;
transition: all 0.15s ease;
}
.toolbar button:hover {
background: #e1e4e8;
}
.toolbar button.is-active {
background: #0969da;
color: #fff;
}
.toolbar .divider {
width: 1px;
height: 20px;
background: #d0d7de;
margin: 0 4px;
}
/* Editing Area & Markdown Preview Area */
.content-area {
display: flex;
min-height: 450px;
}
.editor-wrapper {
flex: 1;
padding: 1.5rem;
cursor: text;
}
/* TipTap Editor Content Typography Styles */
.tiptap {
outline: none;
min-height: 100%;
}
.tiptap p { margin-bottom: 0.8em; line-height: 1.6; }
.tiptap h1 { font-size: 1.8rem; margin: 1rem 0 0.5rem; }
.tiptap h2 { font-size: 1.4rem; margin: 0.8rem 0 0.4rem; }
.tiptap h3 { font-size: 1.15rem; margin: 0.6rem 0 0.3rem; }
.tiptap ul, .tiptap ol { padding-left: 1.5rem; margin-bottom: 0.8em; }
.tiptap blockquote {
border-left: 4px solid #d0d7de;
padding-left: 1rem;
color: #57606a;
margin: 0.8em 0;
}
.tiptap code {
background-color: #eff1f3;
padding: 0.2em 0.4em;
border-radius: 4px;
font-family: monospace;
font-size: 0.85em;
}
.tiptap pre {
background: #24292e;
color: #f6f8fa;
padding: 1rem;
border-radius: 6px;
margin: 0.8em 0;
overflow-x: auto;
}
.tiptap pre code {
background: none;
color: inherit;
padding: 0;
}
/* Markdown Plain Text Viewer Drawer */
#markdown-output {
flex: 1;
border: none;
border-left: 1px solid #e1e4e8;
background: #f6f8fa;
padding: 1.5rem;
font-family: monospace;
font-size: 13px;
resize: none;
outline: none;
white-space: pre-wrap;
display: none; /* Hidden by default, displayed on button click */
}
#markdown-output.show {
display: block;
}
</style>
</head>
<body>
<div class="editor-container">
<!-- Top Toolbar -->
<div class="toolbar" id="toolbar">
<button data-action="heading" data-level="1">H1</button>
<button data-action="heading" data-level="2">H2</button>
<button data-action="heading" data-level="3">H3</button>
<div class="divider"></div>
<button data-action="bold"><b>B</b></button>
<button data-action="italic"><i>I</i></button>
<button data-action="strike"><s>S</s></button>
<button data-action="code">Inline Code</button>
<div class="divider"></div>
<button data-action="bulletList">• Bullet List</button>
<button data-action="orderedList">1. Ordered List</button>
<button data-action="blockquote">Blockquote</button>
<button data-action="codeBlock"></> Code Block</button>
<div class="divider"></div>
<button data-action="undo">Undo</button>
<button data-action="redo">Redo</button>
<div class="divider"></div>
<button id="toggle-md-btn" style="margin-left: auto; color: #0969da;">View Markdown</button>
</div>
<!-- Content Editing Area -->
<div class="content-area">
<div class="editor-wrapper" onclick="document.querySelector('.tiptap').focus()">
<div id="editor"></div>
</div>
<textarea id="markdown-output" readonly placeholder="Markdown source code will update here in real time..."></textarea>
</div>
</div>
<!-- Load latest TipTap modules using ESM -->
<script type="module">
import { Editor } from 'https://esm.sh/@tiptap/core@latest';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@latest';
import { Markdown } from 'https://esm.sh/tiptap-markdown@latest';
// Initialize content
const initialMarkdown = ``;
const mdOutput = document.getElementById('markdown-output');
const toggleBtn = document.getElementById('toggle-md-btn');
// Instantiate Editor
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [
StarterKit,
Markdown.configure({
html: false, // Pure Markdown only, do not output HTML tags
tightLists: true, // Use tight list syntax
bulletListMarker: '-', // Use '-' as the bullet list marker
})
],
content: initialMarkdown,
onTransaction() {
updateToolbarState();
if (mdOutput.classList.contains('show')) {
updateMarkdownView();
}
}
});
// Update Markdown view in real time
function updateMarkdownView() {
mdOutput.value = editor.storage.markdown.getMarkdown();
}
// Toggle Raw Markdown viewer
toggleBtn.addEventListener('click', () => {
const isShowing = mdOutput.classList.toggle('show');
toggleBtn.classList.toggle('is-active', isShowing);
toggleBtn.innerText = isShowing ? 'Hide Markdown' : 'View Markdown';
if (isShowing) {
updateMarkdownView();
}
});
// Update toolbar active states
function updateToolbarState() {
document.querySelectorAll('#toolbar button[data-action]').forEach(btn => {
const action = btn.dataset.action;
const level = btn.dataset.level ? parseInt(btn.dataset.level) : undefined;
const isActive = level
? editor.isActive(action, { level })
: editor.isActive(action);
btn.classList.toggle('is-active', !!isActive);
});
}
// Bind toolbar click events
document.getElementById('toolbar').addEventListener('click', (e) => {
const btn = e.target.closest('button[data-action]');
if (!btn) return;
const action = btn.dataset.action;
const level = btn.dataset.level ? parseInt(btn.dataset.level) : undefined;
const chain = editor.chain().focus();
switch (action) {
case 'bold': chain.toggleBold().run(); break;
case 'italic': chain.toggleItalic().run(); break;
case 'strike': chain.toggleStrike().run(); break;
case 'code': chain.toggleCode().run(); break;
case 'heading': chain.toggleHeading({ level }).run(); break;
case 'bulletList': chain.toggleBulletList().run(); break;
case 'orderedList': chain.toggleOrderedList().run(); break;
case 'blockquote': chain.toggleBlockquote().run(); break;
case 'codeBlock': chain.toggleCodeBlock().run(); break;
case 'undo': chain.undo().run(); break;
case 'redo': chain.redo().run(); break;
}
});
</script>
</body>
</html>Expected Behavior
Don't freeze the page.
Reproducible Example URL (Optional)
No response
Additional Context (Optional)
No response
Source: ueberdosis/tiptap