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

@nldoc/tiptap-extensions

v1.1.0

Published

Custom TipTap extensions, nodes, marks, and schemas for the NLdoc editor

Downloads

225

Readme

@nldoc/tiptap-extensions

Custom TipTap extensions, nodes, marks, and schemas for accessible, validated rich-text editing.

Features

  • NLDocStarterKit: Pre-configured bundle of extensions for accessible, validated rich-text editing
  • Custom Nodes: Document with asset management, headings (including ARIA levels > 6), images, tables, definition lists, and more
  • Custom Marks: Metadata tracking, link with purpose (aria-label), inline quotations
  • Custom Extensions: Automatic UUID identifier assignment, validation findings integration, enhanced content setting
  • Zod Schemas: Runtime validation and TypeScript types for all TipTap JSON content
  • Accessibility First: ARIA attributes, semantic HTML, decorative image support
  • Configurable: All hardcoded text is configurable, no forced theming

Installation

npm install @nldoc/tiptap-extensions

Peer Dependencies

This package requires the following peer dependencies:

npm install @tiptap/core @tiptap/pm @tiptap/react prosemirror-model prosemirror-state zod react

Quick Start

Using NLDocStarterKit (Recommended)

The simplest way to get started is with NLDocStarterKit, which bundles all the core NLdoc extensions together:

import { useEditor, EditorContent } from '@tiptap/react'
import { NLDocStarterKit } from '@nldoc/tiptap-extensions'

// Import structural CSS (optional, but recommended)
import '@nldoc/tiptap-extensions/styles'

const editor = useEditor({
  extensions: [
    NLDocStarterKit.configure({
      // Configure heading levels (default: 1-6)
      heading: {
        levels: [1, 2, 3, 4, 5, 6],
      },
      // Configure image options
      image: {
        // Image configuration options
      },
      // Configure node types for identifier system
      identifier: {
        nodeTypes: ['paragraph', 'heading', 'image', 'listItem', 'blockquote'],
      },
      // Configure node types for validation system
      validation: {
        nodeTypes: ['paragraph', 'heading', 'image', 'listItem', 'blockquote'],
      },
    }),
  ],
  content: '<p>Start editing...</p>',
})

return <EditorContent editor={editor} />

What's included in NLDocStarterKit:

  • TipTap StarterKit (Bold, Italic, Strike, Code, BulletList, OrderedList, ListItem, Blockquote, CodeBlock, HorizontalRule, HardBreak, History, Dropcursor, Gapcursor)
  • Custom Document with asset management
  • Custom Paragraph with role handling
  • Custom Heading with ARIA levels > 6 support
  • Custom Image with asset management and decorative support
  • MetaMark for metadata tracking
  • LinkMark with purpose (aria-label) support
  • IdentifierExtension for automatic UUID assignment
  • ValidationFindingsExtension for accessibility validation
  • SetContentAndDocAttributes for content loading

Extending NLDocStarterKit

You can easily extend NLDocStarterKit with additional TipTap extensions or your own custom extensions:

import { NLDocStarterKit, Table, DefinitionList, DefinitionTerm, DefinitionDetails, InlineQuotationMark } from '@nldoc/tiptap-extensions'
import TableRow from '@tiptap/extension-table-row'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import { createLowlight } from 'lowlight'

const lowlight = createLowlight()

// Additional node types beyond what NLDocStarterKit uses by default
const additionalNodeTypes = [
  'table',
  'tableRow',
  'tableCell',
  'tableHeader',
  'definitionList',
  'definitionTerm',
  'definitionDetails',
  'codeBlock',
]

const editor = useEditor({
  extensions: [
    // Start with NLDocStarterKit
    NLDocStarterKit.configure({
      heading: {
        levels: [1, 2, 3, 4, 5, 6],
      },
      image: {},
      codeBlock: false, // Disable default codeBlock to use CodeBlockLowlight instead
      identifier: {
        nodeTypes: [
          'paragraph',
          'heading',
          'image',
          'listItem',
          'blockquote',
          ...additionalNodeTypes,
        ],
      },
      validation: {
        nodeTypes: [
          'paragraph',
          'heading',
          'image',
          'listItem',
          'blockquote',
          ...additionalNodeTypes,
        ],
      },
    }),

    // Add additional marks
    Subscript,
    Superscript,
    InlineQuotationMark,

    // Add code block with syntax highlighting
    CodeBlockLowlight.configure({ lowlight }),

    // Add table support
    Table,
    TableHeader,
    TableRow,
    TableCell,

    // Add definition list support
    DefinitionList.configure({
      defaultTermText: 'Term',
      defaultDetailsText: 'Description',
    }),
    DefinitionTerm,
    DefinitionDetails,
  ],
  content: '<p>Start editing...</p>',
})

This pattern allows you to:

  1. Start with a solid foundation of NLdoc-specific extensions
  2. Disable specific StarterKit extensions if needed (e.g., codeBlock: false)
  3. Add standard TipTap extensions (Subscript, Superscript, etc.)
  4. Add custom NLdoc extensions (Table, DefinitionList, etc.)
  5. Extend the identifier and validation systems to cover your additional node types

Using DEFAULT_TRACKED_NODE_TYPES

To easily extend the default tracked node types without repeating them, use the exported DEFAULT_TRACKED_NODE_TYPES constant:

import { NLDocStarterKit, DEFAULT_TRACKED_NODE_TYPES } from '@nldoc/tiptap-extensions'

const additionalNodeTypes = ['table', 'tableRow', 'tableCell', 'codeBlock']

const editor = useEditor({
  extensions: [
    NLDocStarterKit.configure({
      identifier: {
        nodeTypes: [...DEFAULT_TRACKED_NODE_TYPES, ...additionalNodeTypes],
      },
      validation: {
        nodeTypes: [...DEFAULT_TRACKED_NODE_TYPES, ...additionalNodeTypes],
      },
    }),
    // ... your additional extensions
  ],
})

DEFAULT_TRACKED_NODE_TYPES includes:

  • paragraph
  • heading
  • image
  • bulletList
  • orderedList
  • listItem
  • blockquote

Configuration

NLDocStarterKit Options

interface NLDocStarterKitOptions {
  // All StarterKit options are supported
  // See: https://tiptap.dev/docs/editor/api/extensions/starter-kit

  // Custom heading HTML attributes
  customHeadingHTMLAttributes?: HeadingOptions['HTMLAttributes']

  // Image configuration
  image: Partial<ImageOptions>

  // Node types for identifier system
  identifier?: {
    nodeTypes: string[]
  }

  // Node types for validation system
  validation?: {
    nodeTypes: string[]
  }

  // Link configuration (set to false to disable)
  link?: false | Partial<LinkOptions>
}

Disabling StarterKit Extensions

You can disable any StarterKit extension by setting it to false:

NLDocStarterKit.configure({
  codeBlock: false,  // Disable codeBlock
  dropcursor: false, // Disable dropcursor
  gapcursor: false,  // Disable gapcursor
})

Configuring Definition List Text

The DefinitionList node allows you to configure the default text for new terms and descriptions:

import { DefinitionList } from '@nldoc/tiptap-extensions'

DefinitionList.configure({
  defaultTermText: 'Nieuw term',        // Dutch
  defaultDetailsText: 'Beschrijving',   // Dutch
})

// Or in English
DefinitionList.configure({
  defaultTermText: 'New term',
  defaultDetailsText: 'Description',
})

Working with Validation

Setting Validation Findings

import type { ValidationFinding } from '@nldoc/types'

// Fetch validation findings from your API
const findings: ValidationFinding[] = [
  {
    type: 'https://spec.validation.nldoc.nl/Resource/ValidationFinding',
    resourceId: 'node-uuid',
    severity: 'error',
    rule: 'image-alt',
    ruleset: 'wcag',
    rulesetVersion: '2.1',
  },
]

// Apply to editor
editor.commands.setValidationFindings(findings)

This will:

  1. Map findings to nodes by resourceId (UUID)
  2. Add validationFindingIds array to each node (rendered as aria-describedby)
  3. Add highestSeverity class (error/warning/notice) to each node
  4. Update text marks with validation info

Required Validation CSS

If you use the ValidationFindingsExtension, style these classes in your application:

/* Severity indicators */
.tiptap .error {
  border-bottom: 2px solid red;
}

.tiptap .warning {
  border-bottom: 2px dashed orange;
}

.tiptap .notice {
  border-bottom: 2px dotted blue;
}

/* Focus states */
.tiptap .error.has-focus {
  background-color: rgba(255, 0, 0, 0.1);
}

.tiptap .warning.has-focus {
  background-color: rgba(255, 165, 0, 0.1);
}

.tiptap .notice.has-focus {
  background-color: rgba(0, 0, 255, 0.1);
}

Styling

Import Structural CSS

import '@nldoc/tiptap-extensions/styles'

The provided CSS contains only structural styles (layout, positioning, basic formatting). All theming (colors, fonts, spacing) should be provided by your application.

Table Styling

.table-wrapper {
  overflow-x: auto;
  border-radius: 8px;
}

.tiptap table {
  border-collapse: collapse;
}

.tiptap th,
.tiptap td {
  border: 1px solid #ddd;
  padding: 8px;
}

.tiptap th {
  background-color: #f0f0f0;
  font-weight: bold;
}

.tiptap .selectedCell {
  background-color: #b3d4fc;
}

Zod Schemas

All TipTap JSON content can be validated at runtime using the provided Zod schemas.

import {
  TipTapDocumentDto,
  TipTapHeadingDto,
  TipTapParagraphDto,
  TipTapImageDto,
} from '@nldoc/tiptap-extensions'

// Validate document structure
const document = TipTapDocumentDto.parse(jsonContent)

// TypeScript types are automatically inferred
type Document = z.infer<typeof TipTapDocumentDto>

API Reference

Below is detailed documentation for all individual extensions, nodes, and marks included in this package. Most users should start with NLDocStarterKit (see Quick Start above), but these references are useful for advanced configuration or when building custom extensions.

Custom Nodes

Document

Extends TipTap's Document with an assets attribute for storing base64-encoded images.

import { Document, hasAssets } from '@nldoc/tiptap-extensions'

// Check if document has assets
if (hasAssets(doc)) {
  console.log(doc.attrs.assets) // Record<string, string>
}

Heading

Supports heading levels 1-6 as native HTML elements (<h1> through <h6>), and levels 7+ using <p role="heading" aria-level="N"> for accessibility.

editor.commands.setHeading({ level: 1 })  // <h1>
editor.commands.setHeading({ level: 7 })  // <p role="heading" aria-level="7">

// Keyboard shortcuts: Mod-Alt-1 through Mod-Alt-6
// Input rules: # through ######

Image

Image node with asset management and decorative image support.

// Add an image with alt text
editor.commands.setImage({
  base64: 'data:image/png;base64,...',
  alt: 'Description of image',
  decorative: false,
})

// Add a decorative image (alt will be set to empty string)
editor.commands.setImage({
  base64: 'data:image/png;base64,...',
  decorative: true,
})

Images are automatically stored in the document's assets attribute with UUID keys. Unused assets are automatically cleaned up.

Table

Table with wrapper div for responsive scrolling.

import { Table } from '@nldoc/tiptap-extensions'
import TableRow from '@tiptap/extension-table-row'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'

// Use with standard TipTap table extensions
extensions: [
  Table,
  TableRow,
  TableCell,
  TableHeader,
]

Definition Lists

Semantic HTML definition lists (<dl>, <dt>, <dd>).

// Insert a definition list
editor.commands.insertDefinitionList()

// Keyboard behavior:
// - Press Enter at end of term → creates definition details
// - Press Enter at end of details → creates new term + details pair

Paragraph

Extends TipTap's Paragraph to prevent conflicts with Heading's role="heading" attribute.

Custom Marks

MetaMark

Invisible mark that wraps text nodes with metadata (ID, validation severity, validation finding IDs). Essential for the identifier and validation systems.

import { MetaMark, getMetaMark, upsertMark } from '@nldoc/tiptap-extensions'

// Get meta mark from a node
const mark = getMetaMark(node)
console.log(mark.attrs.id)
console.log(mark.attrs.highestSeverity) // 'error' | 'warning' | 'notice' | null
console.log(mark.attrs.validationFindingIds) // string[]

// Update meta mark attributes
const transaction = upsertMark(editorState, transaction, { node, pos }, {
  id: 'some-uuid',
  highestSeverity: 'error',
  validationFindingIds: ['finding-1', 'finding-2'],
})

LinkMark

Extends TipTap's Link with a purpose attribute (rendered as aria-label) for better accessibility.

editor.commands.setLink({
  href: 'https://example.com',
  purpose: 'Visit our documentation',
})

InlineQuotationMark

Renders as <q> element with optional cite attribute.

editor.commands.setInlineQuotation({
  cite: 'https://example.com/source',
})

editor.commands.toggleInlineQuotation()
editor.commands.unsetInlineQuotation()

Custom Extensions

IdentifierExtension

Automatically assigns UUID identifiers to all nodes and text (via MetaMark). Prevents duplicate IDs when nodes are split (e.g., splitting a paragraph).

import { IdentifierExtension, getIdentifier } from '@nldoc/tiptap-extensions'

IdentifierExtension.configure({
  nodeTypes: ['heading', 'paragraph', 'image'],
})

// Get identifier from any node
const id = getIdentifier(node) // Returns string | null

ValidationFindingsExtension

Integrates accessibility validation findings into the editor. Maps findings to nodes/text and applies visual styling via CSS classes.

import { ValidationFindingsExtension } from '@nldoc/tiptap-extensions'
import type { ValidationFinding } from '@nldoc/types'

ValidationFindingsExtension.configure({
  nodeTypes: ['heading', 'paragraph', 'image'],
})

// Set validation findings
const findings: ValidationFinding[] = [
  {
    type: 'https://spec.validation.nldoc.nl/Resource/ValidationFinding',
    resourceId: 'node-uuid',
    severity: 'error',
    rule: 'missing-alt-text',
    ruleset: 'wcag',
    rulesetVersion: '2.1',
  },
]

editor.commands.setValidationFindings(findings)

This adds the following attributes to nodes:

  • validationFindingIds: Array of finding IDs (rendered as aria-describedby)
  • highestSeverity: 'error' | 'warning' | 'notice' (rendered as CSS class)

SetContentAndDocAttributes

Custom command to set editor content while preserving document-level attributes (useful for loading documents from storage).

editor.commands.setContentAndDocAttributes(
  jsonContent,
  true, // emitUpdate
  {}, // parseOptions
  { errorOnInvalidContent: false }
)

Utilities

findingHTMLId

Creates unique HTML IDs for validation findings.

import { findingHTMLId } from '@nldoc/tiptap-extensions'

const id = findingHTMLId(finding)
// Returns: "comment-{resourceId}-{encodedRuleInfo}-{severity}"

Index and IndexWithDefault

Simple index data structures for efficient lookups.

import { Index, IndexWithDefault } from '@nldoc/tiptap-extensions'

const index = new Index({ key1: 'value1', key2: 'value2' })
index.get('key1') // 'value1'
index.get('key3') // undefined

const indexWithDefault = new IndexWithDefault({ key1: 'value1' }, 'default')
indexWithDefault.get('key1') // 'value1'
indexWithDefault.get('key3') // 'default'

Transaction Metadata Constants

import { TransactionMeta } from '@nldoc/tiptap-extensions'

// Prevent validation from being triggered
transaction.setMeta(TransactionMeta.blockValidation, true)

// Prevent transaction from being added to history
transaction.setMeta(TransactionMeta.addToHistory, false)

TypeScript

This package is fully typed. All exports include TypeScript definitions.

import type {
  NLDocStarterKitOptions,
  HeadingOptions,
  ImageOptions,
  InlineQuotationOptions,
  DefinitionListOptions,
  MetaMarkAttributes,
} from '@nldoc/tiptap-extensions'

License

EUPL-1.2

Contributing

This package is part of the NLdoc project. Contributions are welcome!

Credits

Built with TipTap and ProseMirror.