diff --git a/examples/advanced-blog/src/app/(web)/posts/[slug]/page.tsx b/examples/advanced-blog/src/app/(web)/posts/[slug]/page.tsx
index f5d777ed..b98b9f92 100644
--- a/examples/advanced-blog/src/app/(web)/posts/[slug]/page.tsx
+++ b/examples/advanced-blog/src/app/(web)/posts/[slug]/page.tsx
@@ -57,7 +57,7 @@ export default async function Post(params: Params) {
diff --git a/examples/advanced-blog/src/components/mdx/mdx-component.tsx b/examples/advanced-blog/src/components/mdx/mdx-component.tsx
index 73ec9377..b547ed6d 100644
--- a/examples/advanced-blog/src/components/mdx/mdx-component.tsx
+++ b/examples/advanced-blog/src/components/mdx/mdx-component.tsx
@@ -1,9 +1,10 @@
-"use client";
-import { getMDXComponent } from "mdx-bundler/client";
-import Image from "next/image";
-import { ImgHTMLAttributes, useMemo } from "react";
-import { CustomCode, Pre } from "./custom-code";
-import CustomLink from "./custom-link";
+'use client'
+import { getMDXComponent } from 'mdx-bundler/client'
+import Image from 'next/image'
+import { ImgHTMLAttributes, useMemo, useRef, useEffect, useState } from 'react'
+import { CustomCode, Pre } from './custom-code'
+import CustomLink from './custom-link'
+import TOC from './toc'
const MDXComponentsMap = {
a: CustomLink,
@@ -12,30 +13,51 @@ const MDXComponentsMap = {
),
pre: Pre,
- code: CustomCode,
-};
+ code: CustomCode
+}
type MDXComponentProps = {
- content: string;
- components?: Record;
-};
+ content: string
+ components?: Record
+ showTOC?: boolean
+}
export const MDXComponent = ({
content,
components = {},
+ showTOC = false
}: MDXComponentProps) => {
- const Component = useMemo(() => getMDXComponent(content), [content]);
+ const [renderedContent, setRenderedContent] = useState('')
+ const contentRef = useRef(null)
+
+ const Component = useMemo(() => getMDXComponent(content), [content])
+
+ useEffect(() => {
+ if (contentRef.current) {
+ setRenderedContent(contentRef.current.innerHTML)
+ }
+ }, [content])
+
+ const shouldShowTOC = useMemo(() => {
+ if (showTOC === true) return true
+ if (showTOC === false) return false
+ }, [showTOC])
return (
-
- );
-};
-
-export default MDXComponent;
+ <>
+ {shouldShowTOC && }
+
+
+
+ >
+ )
+}
+
+export default MDXComponent
diff --git a/examples/advanced-blog/src/components/mdx/toc.tsx b/examples/advanced-blog/src/components/mdx/toc.tsx
new file mode 100644
index 00000000..52c264a3
--- /dev/null
+++ b/examples/advanced-blog/src/components/mdx/toc.tsx
@@ -0,0 +1,80 @@
+import React, { useEffect, useState, useMemo } from 'react'
+import { twMerge } from 'tailwind-merge'
+
+interface TOCItem {
+ depth: number
+ value: string
+ url: string
+}
+
+interface TOCProps {
+ content: string
+ maxDepth?: number
+ className?: string
+ headingClassName?: string
+ title?: string
+}
+
+const TOC: React.FC = ({
+ content,
+ maxDepth = 3,
+ className = 'toc',
+ headingClassName = 'toc-heading',
+ title = 'Table of Contents'
+}) => {
+ const [headings, setHeadings] = useState([])
+
+ const extractHeadings = useMemo(
+ () => () => {
+ try {
+ const tempDiv = document.createElement('div')
+ tempDiv.innerHTML = content
+
+ const headingElements = tempDiv.querySelectorAll(
+ 'h1, h2, h3, h4, h5, h6'
+ )
+ return Array.from(headingElements)
+ .filter((el) => parseInt(el.tagName[1]) <= maxDepth)
+ .map((el) => ({
+ depth: parseInt(el.tagName[1]),
+ value: el.textContent?.trim() || '',
+ url: `#${el.id}`
+ }))
+ } catch (error) {
+ console.error('Error extracting headings:', error)
+ return []
+ }
+ },
+ [content, maxDepth]
+ )
+
+ useEffect(() => {
+ setHeadings(extractHeadings())
+ }, [extractHeadings])
+
+ if (headings.length === 0) {
+ return null
+ }
+
+ return (
+
+ )
+}
+
+export default TOC