npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tiptap-redlines-extension

v1.0.0

Published

Track changes (redlines) extension for TipTap v3. Adds Microsoft Word-style revision tracking — insertions shown in color with underline, deletions shown with strikethrough.

Readme

tiptap-redlines-extension

Track changes (redlines) extension for TipTap v3. Adds Microsoft Word-style revision tracking to any TipTap editor — insertions are highlighted with color and underline, deletions are preserved with strikethrough.

Built for TipTap v3. Based on chenyuncai/tiptap-track-change-extension, rewritten to use TipTap v3's addStorage() API instead of the v2 extension-lookup pattern.

Features

  • Insertion tracking — New text is wrapped in <insert> tags
  • Deletion tracking — Deleted text is preserved and wrapped in <delete> tags (strikethrough)
  • Accept/Reject — Accept or reject individual changes or all changes at once
  • User attribution — Attach user ID and nickname to each change
  • CJK input support — Handles IME composition for Chinese/Japanese/Korean input
  • Y.js compatible — Ignores changes from collaborative sync

Installation

npm install tiptap-redlines-extension

Peer dependencies:

npm install @tiptap/core @tiptap/pm

Quick Start

import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { RedlinesExtension } from 'tiptap-redlines-extension'

const editor = new Editor({
  extensions: [
    StarterKit,
    RedlinesExtension.configure({
      enabled: false, // start with tracking disabled
      onStatusChange: (enabled) => {
        console.log('Track changes:', enabled ? 'ON' : 'OFF')
      },
    }),
  ],
  content: '<p>Hello World</p>',
})

Usage

Enable/Disable Track Changes

// Enable
editor.commands.setTrackChangeStatus(true)

// Disable
editor.commands.setTrackChangeStatus(false)

// Toggle
editor.commands.toggleTrackChangeStatus()

Accept and Reject Changes

// Accept the change at cursor position or within selection
editor.commands.acceptChange()

// Reject the change at cursor position or within selection
editor.commands.rejectChange()

// Accept all changes in the document
editor.commands.acceptAllChanges()

// Reject all changes in the document
editor.commands.rejectAllChanges()

User Attribution

editor.commands.updateOpUserOption('user-123', 'Jane Smith')

Each tracked change stores data-op-user-id, data-op-user-nickname, and data-op-date attributes on the mark element.

Styling

The extension renders insertions as <insert> elements and deletions as <delete> elements. Add CSS to style them:

/* Insertions — blue underline */
.ProseMirror insert {
  color: #2563EB;
  text-decoration: underline;
  text-decoration-color: #2563EB;
  text-underline-offset: 2px;
}

/* Deletions — red strikethrough */
.ProseMirror delete {
  color: #E11D48;
  text-decoration: line-through;
  text-decoration-color: #E11D48;
  opacity: 0.7;
}

Or use any colors that match your design system.

React Example

import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { RedlinesExtension } from 'tiptap-redlines-extension'

function MyEditor() {
  const [isTracking, setIsTracking] = useState(false)

  const editor = useEditor({
    extensions: [
      StarterKit,
      RedlinesExtension.configure({
        enabled: false,
        onStatusChange: setIsTracking,
      }),
    ],
    content: '<p>Start editing...</p>',
  })

  return (
    <div>
      <div className="toolbar">
        <button onClick={() => editor?.commands.toggleTrackChangeStatus()}>
          {isTracking ? 'Suggesting' : 'Editing'}
        </button>
        <button onClick={() => editor?.commands.acceptAllChanges()}>
          Accept All
        </button>
        <button onClick={() => editor?.commands.rejectAllChanges()}>
          Reject All
        </button>
      </div>
      <EditorContent editor={editor} />
    </div>
  )
}

Vue 3 Example

<script setup>
import { useEditor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import { RedlinesExtension } from 'tiptap-redlines-extension'

const isTracking = ref(false)

const editor = useEditor({
  extensions: [
    StarterKit,
    RedlinesExtension.configure({
      enabled: false,
      onStatusChange: (status) => { isTracking.value = status },
    }),
  ],
  content: '<p>Start editing...</p>',
})
</script>

<template>
  <div>
    <button @click="editor?.commands.toggleTrackChangeStatus()">
      {{ isTracking ? 'Suggesting' : 'Editing' }}
    </button>
    <EditorContent :editor="editor" />
  </div>
</template>

API Reference

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Whether track changes is enabled on init | | onStatusChange | (enabled: boolean) => void | undefined | Callback when status changes | | dataOpUserId | string | '' | User ID for change attribution | | dataOpUserNickname | string | '' | User nickname for change attribution |

Commands

| Command | Description | |---------|-------------| | setTrackChangeStatus(enabled) | Enable or disable tracking | | getTrackChangeStatus() | Get current tracking status | | toggleTrackChangeStatus() | Toggle tracking on/off | | acceptChange() | Accept change at cursor/selection | | acceptAllChanges() | Accept all changes | | rejectChange() | Reject change at cursor/selection | | rejectAllChanges() | Reject all changes | | updateOpUserOption(id, name) | Set user info for changes |

Exports

| Export | Description | |--------|-------------| | RedlinesExtension | Main extension (default export) | | InsertionMark | TipTap Mark for insertions | | DeletionMark | TipTap Mark for deletions | | MARK_INSERTION | Mark name constant ('insertion') | | MARK_DELETION | Mark name constant ('deletion') |

HTML Output

Insertions render as:

<insert data-op-user-id="..." data-op-user-nickname="..." data-op-date="...">new text</insert>

Deletions render as:

<delete data-op-user-id="..." data-op-user-nickname="..." data-op-date="...">removed text</delete>

How It Works

When tracking is enabled:

  1. Typing new text — The onTransaction hook detects new content via ProseMirror ReplaceStep and applies the insertion mark
  2. Deleting text — Instead of removing content, the extension re-adds it with the deletion mark (red strikethrough)
  3. Deleting tracked insertions — If you delete text that was already marked as an insertion, it's removed for real (since it was a pending suggestion)
  4. Accepting a change — Insertions: the mark is removed (text becomes normal). Deletions: the content is removed for real.
  5. Rejecting a change — Insertions: the content is removed. Deletions: the mark is removed (text is restored).

Compatibility

  • TipTap v3 (@tiptap/core ^3.0.0)
  • Works with @tiptap/react, @tiptap/vue-3, and vanilla JS
  • Compatible with Y.js collaborative editing (ignores sync changes)

Credits

Based on chenyuncai/tiptap-track-change-extension (MIT License). Rewritten for TipTap v3 compatibility using addStorage() instead of the v2 getSelfExt() pattern.

License

MIT