Anchors for headings in MDX
How to add anchors to headings in MDX? It's surprisingly easy to do. I assume you are already familiar with MDX
so somewhere in your code you should have a blog page layout component that uses <MDXProvider>
, like that:
<MDXProvider>{children}</MDXProvider>
MDX consists of components itself. So if there is a code like this
then the interpreter
changes the inner value (children) into the same content but wrapped with <code></code>
.
The same applies to all the headings, lists, paragraphs, etc. Our job is to override the headings. We can do it by passing
components
hash and specifying a replacement.
import H2 from './MyCustomMDX/H2';import H3 from './MyCustomMDX/H3';import H4 from './MyCustomMDX/H4';// ...<MDXProvidercomponents={{h2: H2,h3: H3,h4: H4,}}>{children}</MDXProvider>;
Please notice that we are not going to add an anchor to the <h1>
tag. It doesn't make sense in my opinion. <h1>
is like a summary
of the whole page. The URL that links to it is the direct link to the post. Anchors should be used to specific parts of a post
(to a section).
Override heading component
The override for <h2>
that shows an anchor when the mouse is over the text could look like this:
Available on1// ./MyCustomMDX/H2.js23function getAnchor(text) {4 return text5 .toLowerCase()6 .replace(/[^a-z0-9 ]/g, '')7 .replace(/[ ]/g, '-');8}910const H2 = ({ children }) => {11 const anchor = getAnchor(children);12 const link = `#${anchor}`;13 return (14 <h2 id={anchor}>15 <a href={link} className="anchor-link">16 §17 </a>18 {children}19 </h2>20 );21};2223export default H2;24
Below you'll see the demo. Please hover over the text. On the left you should see §
sign that is also a link, representing
our anchor:
I'm h2 with an anchor
Let's explain a few bits. The way we use headings in Markdown is by using #
sign, for example:
## I'm h2 with an anchor
Everything that goes after ##
is passed as a child to the H2
component.
So the next interesting bit is done in the getAnchor
function. Take a look at lines 3 to 8.
This is what happens:
- line 5 - we convert the input to lower case → "i'm h2 with an anchor"
- line 6 - we remove all non-alphanumeric characters → "im h2 with an anchor"
- line 7 - we replace spaces with a hyphen → "im-h2-with-an-anchor"
... and voilà. We have a URL-friendly anchor 🎉
The styling
Another important thing here is the CSS. We want to show the anchor only on hover and somewhere next to the heading itself:
h2 {position: relative;}.anchor-link {color: #666;opacity: 0;position: absolute;transform: translate(-1em, -2px);width: 1em;}h2:hover .anchor-link {opacity: 1;}
Of course, you can go crazy with your anchors ;) That one is very basic.
Recommendation
One thing that is easy to overlook here (in my example) is using a character like §
inside of <h2>
tag.
In this approach, the sign will become a part of the document outline. Which is not something we want.
It's better to use an icon in SVG format for example but I didn't want to complicate the example.
If the simple sign is what you want then you should render <a>
tag before or after the <h2>
.
If you'd like to see more content like this, please follow me on Twitter: tomekdev_. I usually write about interfaces in the real life. So where the design meets implementation.
Comments and discussion welcomed in this thread.