Created
March 22, 2022 03:58
-
-
Save sobelk/16fe68ff5520b2d5e2b6d406e329e0de to your computer and use it in GitHub Desktop.
React Markdown TOC Proof-of-Concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This is based on an idea for building a table of contents on top of | |
React Markdown using a custom renderer. The original conversation is at | |
https://github.com/remarkjs/react-markdown/issues/48 | |
*/ | |
import React from "react"; | |
import ReactMarkdown from "react-markdown"; | |
import { Row, Col, Container } from "react-bootstrap"; | |
import { HeadingProps } from "react-markdown/lib/ast-to-react"; | |
const markdown: string = require("~static/docs/markdown.md"); | |
export default function Documentation() { | |
const toc: { | |
level: number, | |
id: string, | |
title: string, | |
}[] = []; | |
// Magic. | |
const addToTOC = ({children, ...props}: React.PropsWithChildren<HeadingProps>) => { | |
const level = Number(props.node.tagName.match(/h(\d)/)?.slice(1)); | |
if (level && children && typeof children[0] === "string") { | |
const id = children[0].toLowerCase().replace(/[^a-z0-9]+/g, "-"); | |
toc.push({ | |
level, | |
id, | |
title: children[0], | |
}); | |
return React.createElement( | |
props.node.tagName, {id}, children | |
) | |
} else { | |
return React.createElement(props.node.tagName, props, children); | |
} | |
}; | |
function TOC() { | |
return ( | |
<ul className="table-of-contents"> | |
{toc.map(({level, id, title}) => ( | |
<li key={id} className={`toc-entry-level-${level}`}> | |
<a href={`#${id}`}>{title}</a> | |
</li> | |
))} | |
</ul> | |
); | |
} | |
return ( | |
<Container> | |
<Row> | |
<Col md="9" className="wall-of-text"> | |
<ReactMarkdown | |
components={{ | |
h2: addToTOC, | |
h3: addToTOC, | |
h4: addToTOC, | |
h5: addToTOC, | |
h6: addToTOC, | |
}} | |
> | |
{markdown} | |
</ReactMarkdown> | |
</Col> | |
<Col md="3"> | |
{/* More magic. */} | |
<TOC /> | |
</Col> | |
</Row> | |
</Container> | |
); | |
} | |
Thanks mate, nice one!
I get all headings repeated twice though, but will figure it out.
And this only works when TOC is rendered AFTER <ReactMarkdown />
right? To move it up I use css then.. ie. "flex flex-col flex-col-reverse" if using Tailwind for example.
Did you perhaps get to use this in production?
@trainoasis Have you been able to fix this issue "all headings repeated twice"?
Nice!, thank you
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
what is this one do actually?
{/* More magic. */}