How to add anchor links to headings in Astro
Anchor links are a useful feature for websites that focus mainly on text, such as a project documentation site or a blog. Like when I changed the code blocks style of my site, I went back to the Astro Docs repo to figure out how to add their anchor links to my site, so let me show you what I did to pull it off!
Steps
-
Open your terminal or command prompt and run the following command to navigate to the root of your Astro project:
cd /path/to/your/project
-
Add
rehype-autolink-headings
,html-escaper
,hast-util-to-string
andrehype-slug
packages to your site's dependencies:npm install rehype-autolink-headings npm install html-escaper npm install @types/html-escaper npm install hast-util-to-string npm install rehype-slug
-
Create the folder
plugins
in the root.mkdir plugins
-
Create the file
rehype-autolink-config.ts
inside theplugins
folder:# When using Windows cd . > plugins/rehype-autolink-config.ts # When using Linux or macOS touch plugins/rehype-autolink-config.ts
-
Add the following code to the
plugins/rehype-autolink-config.ts
file:import { toString } from 'hast-util-to-string'; import { h } from 'hastscript'; import { escape } from 'html-escaper'; import type { Options } from 'rehype-autolink-headings'; const AnchorLinkIcon = h( 'span', { ariaHidden: 'true', class: 'anchor-icon' }, h( 'svg', { width: 16, height: 16, version: 1.1, viewBox: '0 0 16 16', xlmns: 'http://www.w3.org/2000/svg', }, h('path', { fillRule: 'evenodd', fill: 'currentcolor', d: 'M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z', }) ) ); const createSROnlyLabel = (text: string) => h( 'span', { 'is:raw': true, class: 'sr-only' }, `${escape(text)}` ); export const autolinkConfig: Options = { properties: { class: 'anchor-link' }, behavior: 'append', content: (heading) => [AnchorLinkIcon, createSROnlyLabel(toString(heading))], };
You can change the position of the anchor link icon by changing the value of
behavior
to one of these:-
prepend
: The anchor link<a>
and its associated elements are placed before the content of the heading, meaning that the anchor link is displayed visually as part of the heading.<h2 id="steps"> <a class="anchor-link" href="#steps"> <span aria-hidden="true" class="anchor-icon"><svg ... ></svg></span> <span is:raw="true" class="sr-only">Steps</span> </a> Steps </h2>
-
append
: The anchor link<a>
and its associated elements are placed after the content of the heading, meaning that the anchor link is displayed visually as part of the heading.<h2 id="steps"> Steps <a class="anchor-link" href="#steps"> <span aria-hidden="true" class="anchor-icon"><svg ... ></svg></span> <span is:raw="true" class="sr-only">Steps</span> </a> </h2>
-
before
: The anchor link<a>
and its associated elements appear before the heading element.<a class="anchor-link" href="#steps"> <span aria-hidden="true" class="anchor-icon"><svg ... ></svg></span> <span is:raw="true" class="sr-only">Steps</span> </a> <h2 id="steps">Steps</h2>
-
after
The anchor link<a>
and its associated elements appear after the heading element.<h2 id="steps">Steps</h2> <a class="anchor-link" href="#steps"> <span aria-hidden="true" class="anchor-icon"><svg ... ></svg></span> <span is:raw="true" class="sr-only">Steps</span> </a>
-
-
In the
.css
file containing your site's styles, add the following code:/* ===== anchor link ===== */ .anchor-link { @apply ml-2 text-skin-accent; } .anchor-link:hover { @apply text-skin-base; }
Note that I'm using Tailwind and that the text color is defined in my
tailwind.config.cjs
file. You can use any color you like, or even remove the::hover
selector if you don't want the anchor icon to change on hover.A plain CSS example would be:
/* ===== anchor link ===== */ .anchor-link { margin-left: 0.5 rem; color: rgb(255, 107, 1); }
-
Add the following code to the
astro.config.*
:import rehypeSlug from "rehype-slug"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import { autolinkConfig } from "./plugins/rehype-autolink-config"; export default defineConfig({ markdown: { rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, autolinkConfig]], ... }, ... })
That's it!