Skip to content

Instantly share code, notes, and snippets.

@sobelk
Created March 22, 2022 03:58
Show Gist options
  • Save sobelk/16fe68ff5520b2d5e2b6d406e329e0de to your computer and use it in GitHub Desktop.
Save sobelk/16fe68ff5520b2d5e2b6d406e329e0de to your computer and use it in GitHub Desktop.
React Markdown TOC Proof-of-Concept
/*
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>
);
}
@f4ez
Copy link

f4ez commented May 26, 2023

what is this one do actually?

{/* More magic. */}

@trainoasis
Copy link

trainoasis commented Jul 13, 2023

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?

@HuyAms
Copy link

HuyAms commented Nov 27, 2023

@trainoasis Have you been able to fix this issue "all headings repeated twice"?

@SchwarzAmihay
Copy link

Nice!, thank you

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