Skip to content

bug(richtext-lexical): pasting an <hr> into the editor throws "Create node: Type horizontalrule ... does not match registered node ... with the same type" #18262

Description

@MSelcukAkbas

Describe the Bug

Pasting HTML that contains an <hr> element into a Lexical richText field in the admin UI throws immediately and the paste is dropped. The console shows:

Error: Create node: Type horizontalrule in node HorizontalRuleServerNode does not match registered node HorizontalRuleNode with the same type

(followed by a secondary TypeError: Cannot read properties of undefined (reading 'values') from Lexical's garbage collector, since the editor is left in an inconsistent state after the first error.)

Root cause

HorizontalRuleServerNode.importDOM() (packages/richtext-lexical/src/features/horizontalRule/server/nodes/HorizontalRuleNode.tsx) hardcodes its DOM-paste conversion to always construct a HorizontalRuleServerNode:

static override importDOM(): DOMConversionMap | null {
  return {
    hr: () => ({
      conversion: $convertHorizontalRuleElement, // always returns $createHorizontalRuleServerNode()
      priority: 0,
    }),
  }
}

The admin/editor client, however, registers the client subclass HorizontalRuleNode (packages/richtext-lexical/src/features/horizontalRule/client/nodes/HorizontalRuleNode.tsx) under the same Lexical node type 'horizontalrule'. That subclass does not override importDOM, so it inherits the base implementation above — which is hardcoded to the base class rather than being polymorphic over this.

When Lexical's internal HTML→Lexical converter runs importDOM during paste, it ends up trying to instantiate HorizontalRuleServerNode even though the editor's node registry expects 'horizontalrule' to produce a HorizontalRuleNode instance, which throws Lexical's built-in class-identity check (errorOnTypeKlassMismatch).

Note importJSON does not have this problem, because the client subclass explicitly overrides it and calls its own $createHorizontalRuleNode(). It's specifically the DOM-paste path that's affected.

Reproduction Steps

  1. npx create-payload-app (or clone templates/blank) with Payload 3.90.1, default lexicalEditor(), any DB adapter.
  2. Add any collection with a richText field (default Lexical editor, no custom node config needed — the default set already includes horizontalRule).
  3. Open the admin UI, create a new document, click into the richText field.
  4. Paste HTML containing an <hr>, e.g. copy <p>a</p><hr><p>b</p> to the clipboard (or paste from any source that produces an <hr> — a Markdown-rendered page, Word content, another Lexical instance, etc.) and paste it into the field.

Expected: the horizontal rule (and surrounding paragraphs) is inserted into the editor.

Actual: the console throws the error above and the paste is dropped; the editor content does not change.

Environment

  • payload: 3.90.1
  • @payloadcms/richtext-lexical: 3.90.1
  • @payloadcms/db-sqlite (adapter used in reproduction; not adapter-specific — this is purely a Lexical node-registration bug and is independent of the database)
  • next: 16.3.3
  • react / react-dom: 19.2.6
  • Node: v24.14.0
  • OS: Windows 11

Confirmed still present (code unchanged) on 3.91.0-internal.6e3dc85 and 4.0.0-canary.36 as of 2026-09-23.

Suggested fix

Verified locally: build the node in importDOM from this instead of the hardcoded base class, so a subclass registered under the same type produces an instance of itself. After applying this change to the installed package, the same paste inserts the <hr> correctly with no console errors.

static override importDOM(): DOMConversionMap | null {
  const NodeClass = this
  return {
    hr: () => ({
      conversion: (): DOMConversionOutput => ({
        node: $applyNodeReplacement(new NodeClass()),
      }),
      priority: 0,
    }),
  }
}

A pull request with this fix will follow shortly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions