#8340·tiptap

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

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">&lt;/&gt; 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