You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Tiptap创建自定义节点以直接渲染前端自定义HTML?

Tiptap自定义HTML节点渲染解决方案

1. 先定义能存储HTML的自定义节点

首先要在自定义节点的schema里添加专门存储HTML内容的属性,确保编辑器能正确保存和解析这个字段:

import { Node } from '@tiptap/core';

export const CustomHTMLNode = Node.create({
  name: 'customHTML',
  group: 'block',
  atom: true, // 标记为原子节点,防止被编辑器拆分

  // 定义存储HTML的属性
  addAttributes() {
    return {
      htmlContent: {
        default: '',
        parseHTML: element => element.getAttribute('data-html'),
        renderHTML: attributes => ({ 'data-html': attributes.htmlContent }),
      },
    };
  },

  // 编辑器内的渲染(可选,比如显示占位或预览)
  renderHTML({ HTMLAttributes }) {
    return ['div', HTMLAttributes, '自定义HTML内容'];
  },

  // 支持从HTML解析到节点
  parseHTML() {
    return [{ tag: 'div[data-html]' }];
  },
});

2. 前端渲染实现类似dangerouslySetInnerHTML的效果

因为generateHTML默认会转义HTML字符串,所以不能直接靠它输出原始HTML,有两种可行方案:

方案一:遍历文档节点分别处理

手动遍历ProseMirror文档的节点,遇到自定义HTML节点就直接提取属性用React的dangerouslySetInnerHTML渲染,其他节点继续用generateHTML处理:

import { generateHTML } from "@tiptap/html";

function renderEditorContent(doc) {
  const contentElements = [];

  doc.content.forEach((node, index) => {
    if (node.type.name === 'customHTML') {
      // 直接渲染自定义HTML内容
      contentElements.push(
        <div key={index} dangerouslySetInnerHTML={{ __html: node.attrs.htmlContent }} />
      );
    } else {
      // 其他节点用常规方法生成HTML
      const nodeHtml = generateHTML(node, [
        StarterKit, 
        RelatedArticleNode,
        InArticleImageNode,
        CustomHTMLNode
      ]);
      contentElements.push(
        <div key={index} dangerouslySetInnerHTML={{ __html: nodeHtml }} />
      );
    }
  });

  return <div className="editor-content">{contentElements}</div>;
}

方案二:自定义HTML生成器

重写generateHTML对自定义节点的处理逻辑,让它直接输出原始HTML,之后整体渲染:

import { generateHTML, HTMLGenerator } from "@tiptap/html";

// 创建自定义HTML生成器
const htmlGenerator = new HTMLGenerator([
  StarterKit, 
  RelatedArticleNode,
  InArticleImageNode,
  CustomHTMLNode
]);

// 注册自定义节点的渲染规则
htmlGenerator.registerNode('customHTML', (node) => {
  return node.attrs.htmlContent;
});

// 生成完整HTML内容
const fullHtml = htmlGenerator.generate(proseMirrorDocument);

// 在React中渲染
<div dangerouslySetInnerHTML={{ __html: fullHtml }} />

3. 额外注意事项

  • XSS风险:直接渲染原始HTML非常危险,必须确保htmlContent的来源是完全可信的(比如仅后台可控或经过严格的HTML过滤)。
  • 编辑器内预览:如果需要在Tiptap编辑器里直接预览HTML内容,可以给自定义节点添加React节点视图:
import { NodeViewWrapper } from '@tiptap/react';

export const CustomHTMLNodeView = ({ node }) => {
  return (
    <NodeViewWrapper>
      <div dangerouslySetInnerHTML={{ __html: node.attrs.htmlContent }} />
    </NodeViewWrapper>
  );
};

// 在CustomHTMLNode的定义里添加:
addNodeView() {
  return CustomHTMLNodeView;
}

内容的提问来源于stack exchange,提问作者Rápli András

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:13:14