
Research
/Security News
Miasma Mini Shai-Hulud Hits ImmobiliareLabs npm Packages
Miasma Mini Shai-Hulud hits @immobiliarelabs Backstage plugins, targeting GitLab and LDAP auth packages on npm.
@nldoc/tiptap-extensions
Advanced tools
Custom TipTap extensions, nodes, marks, and schemas for the NLdoc editor
Custom TipTap extensions, nodes, marks, and schemas for accessible, validated rich-text editing.
npm install @nldoc/tiptap-extensions
This package requires the following peer dependencies:
npm install @tiptap/core @tiptap/pm @tiptap/react prosemirror-model prosemirror-state zod react
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:
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:
codeBlock: false)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:
paragraphheadingimagebulletListorderedListlistItemblockquoteinterface 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>
}
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
})
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',
})
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:
resourceId (UUID)validationFindingIds array to each node (rendered as aria-describedby)highestSeverity class (error/warning/notice) to each nodeIf 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);
}
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-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;
}
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>
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.
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>
}
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 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 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,
]
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
Extends TipTap's Paragraph to prevent conflicts with Heading's role="heading" attribute.
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'],
})
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',
})
Renders as <q> element with optional cite attribute.
editor.commands.setInlineQuotation({
cite: 'https://example.com/source',
})
editor.commands.toggleInlineQuotation()
editor.commands.unsetInlineQuotation()
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
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)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 }
)
Creates unique HTML IDs for validation findings.
import { findingHTMLId } from '@nldoc/tiptap-extensions'
const id = findingHTMLId(finding)
// Returns: "comment-{resourceId}-{encodedRuleInfo}-{severity}"
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'
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)
This package is fully typed. All exports include TypeScript definitions.
import type {
NLDocStarterKitOptions,
HeadingOptions,
ImageOptions,
InlineQuotationOptions,
DefinitionListOptions,
MetaMarkAttributes,
} from '@nldoc/tiptap-extensions'
EUPL-1.2
This package is part of the NLdoc project. Contributions are welcome!
Built with TipTap and ProseMirror.
FAQs
Custom TipTap extensions, nodes, marks, and schemas for the NLdoc editor
We found that @nldoc/tiptap-extensions demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Miasma Mini Shai-Hulud hits @immobiliarelabs Backstage plugins, targeting GitLab and LDAP auth packages on npm.

Security News
Rolldown paused Rust React Compiler integration after a 5MB binary size increase raised concerns about shipping React-specific code to all Vite users.

Security News
/Research
Mini Shai-Hulud expands into the Go ecosystem after hitting LeoPlatform npm packages and targeting GitHub Actions workflows.