27
loading...
This website collects cookies to deliver better user experience
<h1>
, <h2>
, <h3>
, <h4>
, <h5>
, and <h6>
in html) will be be wrapped in links that point to themselves. This allows readers to link to specific headings in your articles, jumping to relevant bits of content without forcing someone to read through an entire article. Generally speaking, it will look something like this:<a href="#some-unique-id">
<h1 id="some-unique-id">My first blog post</h1>
</a>
<a>
tag here has an href
value of #some-unique-id
- this is the id of the heading tag. This is based on an HTML standard defined by the W3C. In short, you can link to any element on an HTML page which has a unique id
attribute defined, by appending #[id]
to the end of the URL, like www.example.com#id-of-the-element
.---
title: Hello, world
---
# A fish called wanda
In this essay, I will explain the difference between...
<h1>A fish called wanda</h1>
<p>In this essay, I will explain the difference between...</p>
Note: This tutorial assumes you're using MDX with NextJS, althought it may be applicable to other systems. Feel free to send me any hurdles you encounter with other frameworks, and I'll try to document them here.
Install rehype-slug
in your project by running npm install --save rehype-slug
or yarn add rehype-slug
Add rehype-slug
to the list of rehype plugins MDX uses. In the case of next.js sites, it is likely wherever you call serialize()
from next-mdx-remote
.
import rehypeSlug from 'rehype-slug';
// ...
const options = {
mdxOptions: {
rehypePlugins: [
rehypeSlug, // add IDs to any h1-h6 tag that doesn't have one, using a slug made from its text
],
},
};
const mdxSource = await serialize(post.content, options);
// ...
serialize()
in several places, so I extracted options
to its own file. This avoids repeated code, and allows me to manage my plugins for MDX from one place.id
property added. For the example above, you'd see:<h1 id="a-fish-called-wanda">A fish called wanda</h1>
www.example.com#a-fish-called-wanda
, and the browser will automatically scroll to the heading.<h1>
- you'd want to extend this for all title tags, i.e. <h1>
through <h6>
:import Link from 'next/link';
const CustomH1 = ({ id, ...rest }) => {
if (id) {
return (
<Link href={`#${id}`}>
<a>
<h1 {...rest} />
</a>
</Link>
);
}
return <h1 {...rest} />;
};
const components = {
h1: CustomH1,
};
// this would also work in pages/_app.js
const Layout = ({ children }) => {
return <MDXProvider components={components}>{children}</MDXProvider>;
};
#
character before headings when they're hovered over. If you're curious about my implementation with Chakra UI, it looks a bit like this:import NextLink from 'next/link';
import { Link, Heading } from '@chakra-ui/react';
const CustomHeading = ({ as, id, ...props }) => {
if (id) {
return (
<Link href={`#${id}`}>
<NextLink href={`#${id}`}>
<Heading
as={as}
display="inline"
id={id}
lineHeight={'1em'}
{...props}
_hover={{
_before: {
content: '"#"',
position: 'relative',
marginLeft: '-1.2ch',
paddingRight: '0.2ch',
},
}}
/>
</NextLink>
</Link>
);
}
return <Heading as={as} {...props} />;
};
const H1 = (props) => <CustomHeading as="h1" {...props} />;
const H2 = (props) => <CustomHeading as="h2" {...props} />;
const H3 = (props) => <CustomHeading as="h3" {...props} />;
const H4 = (props) => <CustomHeading as="h4" {...props} />;
const H5 = (props) => <CustomHeading as="h5" {...props} />;
const H6 = (props) => <CustomHeading as="h6" {...props} />;
const components = {
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
};
// ...etc - components is passed to MDXProvider in my Layout component
<a href="#a-fish-called-wanda">
<h1 id="a-fish-called-wanda">A fish called wanda</h1>
</a>