Making anchor links in Astro

Published on: 2023-03-11

This is a fancy feature useful for referencing fragments of long posts (i.e., not the ones you are likely to encounter on this site šŸ˜‰). It is often encountered on different websites these days, and implementing it may not be obvious for a beginner, although I must admit that the main difficulty for me was finding the right part of the docs where this is described. So,

See this one?

Autogenerating this kind of links requires Markdown Plugins such as the one described here. Go ahead and

npm install rehype-autolink-headings

The next step is configuring the astro.config.mjs. You donā€™t need a separate rehype-slug for this to work because Astro generates its own IDs for markdown headers; however, these IDs have to be ā€œfedā€ to the rehype plugin. In the import section of astro.config.mjs, youā€™ll need

import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';

and in the export - something like

export default defineConfig({
  ...
  markdown: {
    ...
    rehypePlugins: [rehypeHeadingIds, [rehypeAutolinkHeadings, {
    behavior: 'append',
    test: ['h2', 'h3'],
    properties: {
      ariaHidden: true,
      tabIndex: -1,
      class: 'anchor-link text-secondary ms-2'
    }
    }]]
  },
  ...
})

It is required that rehypeHeadingIds be the first element of the rehypePlugins array. The rehype-autolink-headings options are described in the plugin docs. Basically, Iā€™m appending the anchor to (after) the headers (only 2nd and 3rd ones), and adding some custom classes to the anchorā€™s <a>. The last two classes are Bootstrap-specific.

The default elements appended to the markdown post headers now are as such:

<a aria-hidden="" tabindex="-1" class="anchor-link text-secondary ms-2" href=...>
  <span class="icon icon-link"></span>
</a>

They are invisible though - by default thereā€™s no content in the links because youā€™ve got no style. Letā€™s now amend this, e.g., in the respective markdown post layout file (note that is:global is necessary here):

<style is:global>
  .anchor-link {
    text-decoration: none;
    opacity: 0.5;
    transition: all 0.2s;
  }
  .anchor-link:hover {
    opacity: 1.0;
  }
  .icon-link::before {
    content: '#';
  }
</style>

And thatā€™s all.