How to Add Superscripts and Subscripts to Sanity CMS: A Step-by-Step Guide

superscript and subscript decorator options showing in decorators section of Sanity block array field

Have you ever needed to display scientific formulas like H2O or E=mc2 in your Sanity CMS content? Or perhaps you need to add trademark symbols, footnote references, or mathematical expressions to your website? While Sanity CMS offers powerful content management capabilities, it doesn't include superscript and subscript text decorators out of the box. This limitation can be frustrating when working with technical, scientific, or academic content.

In this comprehensive tutorial, I'll walk you through the process of extending Sanity's block content with custom superscript and subscript decorators. These text formatting options are essential for various content types, from scientific articles and legal disclaimers to academic references and product descriptions.

By the end of this tutorial, you'll be able to:

  • Add custom superscript and subscript decorators to your Sanity schema
  • Create React components to render these decorators in the Sanity Studio
  • Implement the necessary frontend code to display formatted text on your website
  • Understand how to extend this approach to other custom text decorators

Prerequisites

Before we dive in, make sure you have:

  • A working Sanity CMS project (version 2.0 or later)
  • Basic knowledge of Sanity schemas and block content
  • Familiarity with React components
  • Access to your project's schema files
  • Node.js and npm installed on your machine

Solution Overview

Sanity CMS uses a system called Portable Text (formerly Block Content) for rich text editing. This format allows for structured content with marks and decorators that can be serialized to different output formats. By default, Sanity includes basic text decorators like bold, italic, underline, and code, but it's designed to be extensible.

Our solution involves:

  1. Extending the block content schema to include superscript and subscript decorators
  2. Creating custom React components to render these decorators in the Sanity Studio
  3. Implementing custom icons for better visual identification
  4. Setting up the frontend serialization to properly render the formatted text

This approach maintains the structured nature of Portable Text while adding the specific formatting options we need. The benefit of this method is that it's non-destructive and follows Sanity's recommended patterns for customization.

superscript and subscript options to Sanity array decorator field

Let's get started with the implementation!

Step 1: Understanding Block Content in Sanity

What are Block Content Decorators?

Before adding custom decorators, it's important to understand how Sanity's block content works. In Sanity, rich text is stored as structured data using the Portable Text specification. Text decorators (also called "marks") are applied to spans of text within blocks.

The default block content in Sanity includes decorators like:

1// Default decorators in Sanity
2decorators: [
3  { title: 'Strong', value: 'strong' },
4  { title: 'Emphasis', value: 'em' },
5  { title: 'Code', value: 'code' },
6  { title: 'Underline', value: 'underline' },
7  { title: 'Strike', value: 'strike-through' },
8]

When a decorator is applied to text, it gets stored in the content's JSON structure like this:

1{
2  "_type": "block",
3  "children": [
4    {
5      "_type": "span",
6      "text": "This is ",
7      "marks": []
8    },
9    {
10      "_type": "span",
11      "text": "bold",
12      "marks": ["strong"]
13    },
14    {
15      "_type": "span",
16      "text": " text",
17      "marks": []
18    }
19  ]
20}

Our goal is to add superscript and subscript as new decorator options, which will be stored similarly in the content structure.

Step 2: Locating Your Schema Files

Finding the Right Schema File

To add custom decorators, we need to modify the schema file that defines your block content. This is typically found in your Sanity project directory structure.

  1. Navigate to your Sanity project folder
  2. Look for the schemas directory
  3. Find the schema file that defines your block content field

The exact location might vary depending on your project structure, but it's commonly found in paths like:

  • schemas/blockContent.js
  • schemas/objects/blockContent.js
  • schemas/schema.js (if you have a simpler project)

If your project has multiple block content definitions, you'll need to modify each one where you want superscript and subscript to be available.

Step 3: Adding Custom Decorators to Your Schema

Modifying the Block Content Schema

Now that you've found your schema file, let's add the superscript and subscript decorators:

1// Before: Default block content schema
2export default {
3  title: 'Block Content',
4  name: 'blockContent',
5  type: 'array',
6  of: [
7    {
8      title: 'Block',
9      type: 'block',
10      styles: [
11        { title: 'Normal', value: 'normal' },
12        { title: 'H1', value: 'h1' },
13        { title: 'H2', value: 'h2' },
14        { title: 'H3', value: 'h3' },
15        { title: 'Quote', value: 'blockquote' },
16      ],
17      lists: [{title: 'Bullet', value: 'bullet' }, {title: 'Number', value: 'number' }],
18      marks: {
19        decorators: [
20          { title: 'Strong', value: 'strong' },
21          { title: 'Emphasis', value: 'em' },
22          { title: 'Code', value: 'code' },
23          { title: 'Underline', value: 'underline' },
24          { title: 'Strike', value: 'strike-through' },
25        ],
26        annotations: [
27          // Annotations like links would be here
28        ]
29      }
30    }
31  ]
32}

Now, let's modify it to add our new decorators:

1// After: Modified block content schema with superscript and subscript
2export default {
3  title: 'Block Content',
4  name: 'blockContent',
5  type: 'array',
6  of: [
7    {
8      title: 'Block',
9      type: 'block',
10      styles: [
11        { title: 'Normal', value: 'normal' },
12        { title: 'H1', value: 'h1' },
13        { title: 'H2', value: 'h2' },
14        { title: 'H3', value: 'h3' },
15        { title: 'Quote', value: 'blockquote' },
16      ],
17      lists: [{ title: 'Bullet', value: 'bullet' }, {title: 'Number', value: 'number' }],
18      marks: {
19        decorators: [
20          { title: 'Strong', value: 'strong' },
21          { title: 'Emphasis', value: 'em' },
22          { title: 'Code', value: 'code' },
23          { title: 'Underline', value: 'underline' },
24          { title: 'Strike', value: 'strike-through' },
25          { title: 'Superscript', value: 'sup' }, // Added superscript
26          { title: 'Subscript', value: 'sub' },   // Added subscript
27        ],
28        annotations: [
29          // Annotations remain unchanged
30        ]
31      }
32    }
33  ]
34}

What we've done here is add two new decorators to the existing list:

  • { title: 'Superscript', value: 'sup' } for superscript text
  • { title: 'Subscript', value: 'sub' } for subscript text

The value properties ('sup' and 'sub') are important as they'll be used as the mark names in the Portable Text structure and correspond to the HTML tags we'll use for rendering.

Step 4: Rendering Decorated Content on the Frontend

Setting Up Frontend Serialization

Now that we've configured Sanity Studio, we need to ensure that our superscript and subscript formatting renders correctly on the frontend. The approach varies depending on the frontend framework you're using, but here's how to do it with the @portabletext/react library:

First, install the necessary package if you haven't already:

1npm install @portabletext/react

Then, create a serializer configuration for your Portable Text content:

1// In your frontend code (React example)
2import { PortableText } from '@portabletext/react'
3
4// Define custom serializers for our marks
5const components = {
6  marks: {
7    sup: ({ children }) => <sup>{children}</sup>,
8    sub: ({ children }) => <sub>{children}</sub>,
9    // Include other mark serializers as needed
10  },
11  // Include other component serializers as needed
12}
13
14// In your component that renders Sanity content
15const MyComponent = ({ blockContent }) => {
16  return (
17    <div className="content">
18      <PortableText
19        value={blockContent}
20        components={components}
21      />
22    </div>
23  )
24}

This configuration tells the Portable Text renderer how to handle our custom 'sup' and 'sub' marks, transforming them into the appropriate HTML elements.

If you're using Next.js with Sanity, the setup might look like this:

1// In your Next.js page or component
2import { PortableText } from '@portabletext/react'
3import { client } from '../lib/sanity'
4
5// Define custom serializers
6const components = {
7  marks: {
8    sup: ({ children }) => <sup>{children}</sup>,
9    sub: ({ children }) => <sub>{children}</sub>,
10  }
11}
12
13// Fetch and render content
14export default function Page({ pageData }) {
15  return (
16    <main>
17      <h1>{pageData.title}</h1>
18      <div className="content">
19        <PortableText
20          value={pageData.content}
21          components={components}
22        />
23      </div>
24    </main>
25  )
26}
27
28export async function getStaticProps() {
29  const pageData = await client.fetch(`
30    *[_type == "page" && slug.current == "my-page"][0]{
31      title,
32      content
33    }
34  `)
35  
36  return {
37    props: {
38      pageData
39    }
40  }
41}

Step 5: Testing Your Implementation

Verifying Everything Works

Now it's time to test your implementation to make sure everything is working correctly:

  1. Start (or restart) your Sanity Studio.
  2. Navigate to a document with a block content field
  3. Select some text and look for the new superscript and subscript buttons in the formatting toolbar
  4. Apply the formatting and verify it appears correctly in the editor
  5. Save the document and deploy your changes if necessary
  6. Check your frontend to ensure the formatting renders correctly there as well

Screenshot: Sanity Studio with superscript and subscript decorators in the formatting toolbar

Common Issues and Troubleshooting

Problem: Decorators Not Appearing in the Toolbar

Symptoms: You've added the decorators to your schema, but they don't appear in the formatting toolbar in Sanity Studio.

Solution: Check that you've:

  • Correctly modified the right schema file
  • Registered the custom components with the block editor
  • Restarted the Sanity Studio after making changes
  • Deployed the changes to your Sanity Studio if you're using a deployed version
1// Double-check your schema configuration
2marks: {
3  decorators: [
4    // Existing decorators...
5    { title: 'Superscript', value: 'sup' }, // Make sure these are added
6    { title: 'Subscript', value: 'sub' },
7  ]
8}

Problem: Formatting Doesn't Appear on the Frontend

Symptoms: The decorators work fine in Sanity Studio, but the formatting doesn't show up on your website.

Solution: Verify that:

  • You've set up the correct serializers for your frontend
  • Your frontend is fetching the latest content from Sanity
  • There are no CSS rules overriding the sup and sub styling

Problem: Inconsistent Styling Across Browsers

Symptoms: The superscript and subscript text looks different in various browsers.

Solution: Add consistent CSS styling to normalize the appearance:

1/* Add this to your global CSS */
2sup, sub {
3  font-size: 75%;
4  line-height: 0;
5  position: relative;
6  vertical-align: baseline;
7}
8
9sup {
10  top: -0.5em;
11}
12
13sub {
14  bottom: -0.25em;
15}

Problem: Editor Performance Issues

Symptoms: The Sanity editor becomes slow after adding custom decorators.

Solution: This is rare but could happen if you have many custom components or complex rendering logic. Simplify your custom components and make sure they're optimized for performance.

Advanced Techniques

Implementing Keyboard Shortcuts

You can enhance the user experience by adding keyboard shortcuts for your new decorators. This is done by modifying your block editor configuration:

1// In your sanityClient.js or similar file
2const customStyles = {
3  // Existing configuration...
4  decorators: [
5    // Existing decorators...
6    {
7      title: 'Superscript',
8      value: 'sup',
9      component: SuperscriptDecorator,
10      icon: SuperscriptIcon,
11      hotkey: 'mod+shift+.'  // Ctrl/Cmd + Shift + .
12    },
13    {
14      title: 'Subscript',
15      value: 'sub',
16      component: SubscriptDecorator,
17      icon: SubscriptIcon,
18      hotkey: 'mod+shift+,'  // Ctrl/Cmd + Shift + ,
19    }
20  ]
21}

Real-world Applications

Now that you have superscript and subscript capabilities in your Sanity CMS, here are some practical uses:

Scientific Content

Display chemical formulas correctly:

  • Water: H2O
  • Carbon dioxide: CO2

Mathematical expressions:

  • E=mc2
  • x2 + y2 = r2
  • Logarithmic notation: log10

Legal and Business Content

Add proper trademark and registered symbols:

  • ProductTM
  • Brand
  • Copyright©

Academic and Reference Content

Create properly formatted citations and footnotes:

  • Reference to a source1
  • Additional information2
  • Footnote markers3

Product Descriptions

Display measurements and units correctly:

  • Screen size: 15.6"
  • Area: 25m2
  • Temperature: 20°C

Conclusion

In this tutorial, you've learned how to extend Sanity CMS with custom superscript and subscript text decorators. We've covered:

  • Understanding Sanity's block content system
  • Adding custom decorators to your schema
  • Creating React components for rendering in the editor
  • Implementing custom icons for better usability
  • Setting up frontend serialization for proper display
  • Troubleshooting common issues
  • Advanced techniques for further customization

These enhancements will allow your content creators to produce more sophisticated and properly formatted content for scientific, academic, legal, and technical purposes.

Don't Navigate Your Migration Alone

ContentWrap can simplify your CMS migration, with proven strategies on reducing costs, transferring data, and designing studios.

Contentwrap migrating old CMSes to Sanity CMS

Next Steps

Now that you understand how to add custom decorators, you might want to explore:

  • Adding more custom formats like highlighting or small caps
  • Creating complex nested decorators
  • Building a complete plugin for text formatting
  • Implementing advanced serialization options for different frontend frameworks

Resources

For more information, check out these resources:

How to Add Superscripts and Subscripts to Sanity CMS: A Step-by-Step Guide - ContentWrap