Anchored Headings for Portable Text

By Knut Melvær

How to automatically generate linked headings with a custom serializer

serializers.js

import PortableText from "@sanity/block-content-to-react";

export const serializers = {
  type: {
    block: (props) => {
      const { node, children } = props
      const { style, _key } = node

      if (/^h\d/.test(style)) {
        const HeadingTag = style;
        // Even though HTML5 allows id to start with a digit, we append it with a letter to avoid various JS methods to act up and make problems
        const headingId = `h${_key}`;
        return (
          <HeadingTag id={headingId}>
            <a 
              href={`#${headingId}`}
              aria-hidden="true"
              tabIndex={-1}
            >#</a>
            <span>{children}</span>
          </HeadingTag>
          )
      }
      // ... you can put in other overrides here
      
      // or return the default ones 👇
      return PortableText.defaultSerializers.types.block(props)
    }
  },
  // more custom types here…
}

This is a minimal example of how you can add extra attributes and markup to headings from Portable Text in React. You'd approach this similarly in other frameworks. Here we have a custom serializer for the block type. It looks after a style property that contains the pattern with the letter h and a digit (so h1, h2, h3, etc).

Then we generate an id from the _key that comes from the block data. This _key will be stable as long as the block (that is, the heading) exists. Alternatively, you can generate a readable id from the heading text data in children.

This markup put's a linked # inside of the heading, but you can of course do it however you want and add styling to it.

Contributor

Other recipes by the contributor

First Published Timestamp Function

Featured contribution
Official(made by Sanity team)

Automatically track when content was first published with a timestamp that sets once and never overwrites, providing reliable publication history for analytics and editorial workflows.

Knut Melvær
Go to First Published Timestamp Function

Automatically tag blog posts

Featured contribution
Official(made by Sanity team)

AI-powered automatic tagging for Sanity blog posts that analyzes content to generate 3 relevant tags, maintaining consistency by reusing existing tags from your content library.

Go to Automatically tag blog posts