Breaks for Portable Text

By Knut Melvær

How to add different types of “breaks” for Portable Text that can be translated to horizontal rules, “read more”, sections, chapters, and so on.

break.js

export default {
  name: 'break',
  type: 'object',
  title: 'Break',
  fields: [
    {
      name: 'style',
      type: 'string',
      options: {
        list: ['break', 'readMore']
      }
    }
  ]
}

portableText.js

export default {
  name: 'portableText',
  type: 'array',
  title: 'Rich text',
  of: [
    {
      type: 'block'
    },
    {
      name: 'break'
    }
  ]
}

serializer.js

/**
  * This serializer is included in a stateful component
  * in order to deal with a “read more” button.
  *
 **/
import React, { useState } from "react";
import PortableText from "@sanity/block-content-to-react";

function RichText(blocks) {
  const [readMore, setReadMore] = useState(true);
  const serializers = {
    types: {
      break: props => {
        const { style } = props.node;
        if (style === "lineBreak") {
          return <hr className="lineBreak" />;
        }
        if (readMore && style === "readMore") {
          return (
            <div className="readMore">
              <button onClick={() => setReadMore(false)}>Read More</button>
            </div>
          );
        }
        return null;
      },
    }
  }
  /**
   * Find the index of the “readMore” block
   */
  const findReadMoreIndex = doc.text.findIndex(
    ({ _type, style }) => _type === "break" && style === "readMore"
  );
  /**
   * Slice the array to the (first) readMore block
   */
  const slicedBlocks = readMore ? blocks.slice(0, findReadMoreIndex + 1) : blocks
  return <PortableText blocks={slicedBlocks} serializers={serializers} />
}

export default RichText

People sometimes wonder how to implement a <hr /> into Portable Text. The reason it's not part of the specification is that a “horizontal rule” is not a very semantic concept and pretty specific to HTML. However, “breaks” are commonly found in different text formats. For example, “page break,” “section break,” “chapter break,” and so on. This implementation lets you define what types of breaks you want to make available in your content model and how you can choose to translate it to a horizontal line in a React/HTML frontend, or a “Read more” button. You can explore a full demo of it here.

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