diff --git a/console/packages/editor/src/extensions/heading/index.ts b/console/packages/editor/src/extensions/heading/index.ts
index 94190c1df7..3b0aa003f4 100644
--- a/console/packages/editor/src/extensions/heading/index.ts
+++ b/console/packages/editor/src/extensions/heading/index.ts
@@ -1,4 +1,4 @@
-import type { Editor, Range } from "@/tiptap/vue-3";
+import { mergeAttributes, type Editor, type Range } from "@/tiptap/vue-3";
 import TiptapParagraph from "@/extensions/paragraph";
 import TiptapHeading from "@tiptap/extension-heading";
 import type { HeadingOptions } from "@tiptap/extension-heading";
@@ -15,8 +15,24 @@ import MdiFormatHeader6 from "~icons/mdi/format-header-6";
 import { markRaw } from "vue";
 import { i18n } from "@/locales";
 import type { ExtensionOptions } from "@/types";
+import { Decoration, DecorationSet, Plugin, PluginKey } from "@/tiptap";
+import { ExtensionHeading } from "..";
+import { generateAnchor } from "@/utils";
 
 const Blockquote = TiptapHeading.extend<ExtensionOptions & HeadingOptions>({
+  renderHTML({ node, HTMLAttributes }) {
+    const hasLevel = this.options.levels.includes(node.attrs.level);
+    const level = hasLevel ? node.attrs.level : this.options.levels[0];
+    const id = generateAnchor(node.textContent);
+    HTMLAttributes.id = id;
+
+    return [
+      `h${level}`,
+      mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
+      0,
+    ];
+  },
+
   addOptions() {
     return {
       ...this.parent?.(),
@@ -265,6 +281,32 @@ const Blockquote = TiptapHeading.extend<ExtensionOptions & HeadingOptions>({
   addExtensions() {
     return [TiptapParagraph];
   },
+  addProseMirrorPlugins() {
+    return [
+      new Plugin({
+        key: new PluginKey("generate-heading-id"),
+        props: {
+          decorations: (state) => {
+            const { doc } = state;
+            const decorations: Decoration[] = [];
+            doc.descendants((node, pos) => {
+              if (node.type.name === ExtensionHeading.name) {
+                const id = generateAnchor(node.textContent);
+                if (node.attrs.id !== id) {
+                  decorations.push(
+                    Decoration.node(pos, pos + node.nodeSize, {
+                      id,
+                    })
+                  );
+                }
+              }
+            });
+            return DecorationSet.create(doc, decorations);
+          },
+        },
+      }),
+    ];
+  },
 });
 
 export default Blockquote;
diff --git a/console/src/utils/anchor.ts b/console/packages/editor/src/utils/anchor.ts
similarity index 100%
rename from console/src/utils/anchor.ts
rename to console/packages/editor/src/utils/anchor.ts
diff --git a/console/packages/editor/src/utils/index.ts b/console/packages/editor/src/utils/index.ts
index 0e86b17702..531a9ab321 100644
--- a/console/packages/editor/src/utils/index.ts
+++ b/console/packages/editor/src/utils/index.ts
@@ -1 +1,2 @@
 export * from "./delete-node";
+export * from "./anchor";
diff --git a/console/src/components/editor/DefaultEditor.vue b/console/src/components/editor/DefaultEditor.vue
index e19557b7bd..e58d538ff6 100644
--- a/console/src/components/editor/DefaultEditor.vue
+++ b/console/src/components/editor/DefaultEditor.vue
@@ -47,7 +47,6 @@ import {
   ToolbarItem,
   Plugin,
   PluginKey,
-  Decoration,
   DecorationSet,
 } from "@halo-dev/richtext-editor";
 import {
@@ -90,7 +89,6 @@ import type { PluginModule } from "@halo-dev/console-shared";
 import { useDebounceFn, useLocalStorage } from "@vueuse/core";
 import { onBeforeUnmount } from "vue";
 import { usePermission } from "@/utils/permission";
-import { generateAnchor } from "@/utils/anchor";
 
 const { t } = useI18n();
 const { currentUserHasPermission } = usePermission();
@@ -295,27 +293,17 @@ onMounted(() => {
         addProseMirrorPlugins() {
           return [
             new Plugin({
-              key: new PluginKey("generate-heading-id"),
+              key: new PluginKey("get-heading-id"),
               props: {
                 decorations: (state) => {
                   const headings: HeadingNode[] = [];
                   const { doc } = state;
-                  const decorations: Decoration[] = [];
-                  doc.descendants((node, pos) => {
+                  doc.descendants((node) => {
                     if (node.type.name === ExtensionHeading.name) {
-                      const id = generateAnchor(node.textContent);
-                      if (node.attrs.id !== id) {
-                        decorations.push(
-                          Decoration.node(pos, pos + node.nodeSize, {
-                            id,
-                          })
-                        );
-                      }
-
                       headings.push({
                         level: node.attrs.level,
                         text: node.textContent,
-                        id,
+                        id: node.attrs.id,
                       });
                     }
                   });
@@ -323,7 +311,7 @@ onMounted(() => {
                   if (!selectedHeadingNode.value) {
                     selectedHeadingNode.value = headings[0];
                   }
-                  return DecorationSet.create(doc, decorations);
+                  return DecorationSet.empty;
                 },
               },
             }),
diff --git a/console/src/utils/__tests__/anchor.spec.ts b/console/src/utils/__tests__/anchor.spec.ts
deleted file mode 100644
index 4d9f63492c..0000000000
--- a/console/src/utils/__tests__/anchor.spec.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { describe, it, expect } from "vitest";
-import { generateAnchor } from "../anchor";
-
-describe("generateAnchor", () => {
-  it("should handle basic text", () => {
-    expect(generateAnchor("Hello World")).toBe("hello-world");
-  });
-
-  it("should trim whitespace", () => {
-    expect(generateAnchor("  Hello World  ")).toBe("hello-world");
-  });
-
-  it("should replace multiple spaces with a single dash", () => {
-    expect(generateAnchor("Hello    World")).toBe("hello-world");
-  });
-
-  it("should handle Chinese characters", () => {
-    expect(generateAnchor("你好")).toBe("%E4%BD%A0%E5%A5%BD");
-  });
-
-  it("should handle special characters", () => {
-    expect(generateAnchor("Hello@#World$")).toBe("hello%40%23world%24");
-  });
-
-  it("should handle empty string", () => {
-    expect(generateAnchor("")).toBe("");
-  });
-});