Linked Headings
Create anchor-linked headings in MDX.
Use remark-slug
with MDX to create IDs based on the heading’s content. If
you’re using Gatsby, add this to your gatsby-plugin-mdx
options.
const remarkSlug = require('remark-slug')module.exports = {plugins: [{resolve: 'gatsby-plugin-mdx',options: {remarkPlugins: [remarkSlug],},},],}
If you’re using Contentlayer, you can use the
remarkSlug
plugin in your contentlayer.config.js
file.
import { defineDocumentType, makeSource } from 'contentlayer/source-files'import remarkSlug from 'remark-slug'export const Post = defineDocumentType(() => ({name: 'Post',filePathPattern: '*.mdx',contentType: 'mdx',fields: {},}))export default makeSource({contentDirPath: 'posts',documentTypes: [Post],mdx: {remarkPlugins: [remarkSlug],rehypePlugins: [],},})
Next, create a components module to pass to MDX context. If you’re using
gatsby-plugin-theme-ui
, add a src/gatsby-plugin-theme-ui/components.js
file.
If you’re not using Gatsby, pass these components to the
MDXProvider
component.
/** @jsxImportSource theme-ui */const heading = (Tag) => (props) => {if (!props.id) return <Tag {...props} />return (<Tag {...props}><a href={`#${props.id}`}>{props.children}</a></Tag>)}const components = {h1: heading('h1'),h2: heading('h2'),h3: heading('h3'),h4: heading('h4'),h5: heading('h5'),h6: heading('h6'),}export default components
Or with TypeScript:
import type { ComponentPropsWithoutRef } from 'react'import { Themed } from '@theme-ui/mdx'const createHeadingWithLink =(Level: 'h2' | 'h3' | 'h4' | 'h5' | 'h6') =>(props: ComponentPropsWithoutRef<'h2'>) =>(<Level {...props}>{props.id && (<ahref={`#${props.id}`}sx={{color: 'inherit',textDecoration: 'none',':hover': {textDecoration: 'underline',},}}>{props.children}</a>)}</Level>)export const components = {h2: createHeadingWithLink('h2'),h3: createHeadingWithLink('h3'),h4: createHeadingWithLink('h4'),h5: createHeadingWithLink('h5'),h6: createHeadingWithLink('h6'),}