How to render a special formatted inline text in blockContent in Sanity.io

20 replies
Last updated: May 19, 2020
if I have a
type
in my blockContent that I’d like to render as sort of inline text just with some special formatting - is that possible? I originally made it an annotation but that’s clearly to lightweight. I want to manage these as documents but as a doc, it’s a full-width element in the editor. Would like it to show jsut like this
AI Update

Yes, you can absolutely render a custom type as inline text with special formatting! What you need is an inline block object, which is different from both annotations and regular block-level objects.

Here's how to set it up:

In your schema, add your custom type to the of array inside the block definition (not at the array level):

{
  name: 'content',
  type: 'array',
  of: [
    {
      type: 'block',
      // This is the key - put inline objects in the block's 'of' property
      of: [
        {
          type: 'myInlineType',
          name: 'myInlineType',
          title: 'My Inline Type',
          fields: [
            // Your document fields here
            {name: 'value', type: 'string'},
            {name: 'metadata', type: 'object', fields: [...]}
          ]
        }
      ]
    }
  ]
}

The critical difference is:

  • Block-level objects: Added to the array's of property → renders full-width
  • Inline objects: Added to the block's of property → renders inline with text
  • Annotations: Lightweight marks on text spans → can't have complex structure

This way, your custom type will appear inline in the editor (you can insert it via the Insert menu while editing text), but you still get the full power of managing it as a proper document with all the fields you need.

You can also customize how it renders in the Studio by adding a custom inlineBlock component to control its appearance in the editor.

When rendering on the front end, you'll need to handle this inline object type in your Portable Text serializer to display it with your special formatting.

so, I’d like to insert a “keyword” with value of
EDITOR
and be able to continue typing text after that.
I can’t seem to get this implementation to flow inside some text, it needs to be on a new line of it’s own
Hi User, I hope I’m following along correctly here. Is adding these as objects instead an option to you? Or otherwise as a reference to a document? If so, you could try this:

blockContent.js
export default {
  name: 'blockContent',
  type: 'array',
  title: 'Block content',
  of: [
    {
      type: 'block',
      title: 'Block',
      styles: [...],
      lists: [...],
      marks: {
        decorators: [...],
        annotations: [...]
      },
      of: [{type: 'keywordRef'}]
    }
  ]
}

keywordRef.js
export default {
  name: 'keywordRef',
  type: 'object',
  title: 'Keyword',
  fields: [
    {
      name: 'keyword',
      type: 'reference',
      to: [
        {
          type: 'keyword'
        }
      ]
    }
  ],
  preview: {
    select: {
      title: 'keyword.title'
    }
  }
}
Hi User, I hope I’m following along correctly here. Is adding these as objects instead an option to you? Or otherwise as a reference to a document? If so, you could try this:

blockContent.js
export default {
  name: 'blockContent',
  type: 'array',
  title: 'Block content',
  of: [
    {
      type: 'block',
      title: 'Block',
      styles: [...],
      lists: [...],
      marks: {
        decorators: [...],
        annotations: [...]
      },
      of: [{type: 'keywordRef'}]
    }
  ]
}

keywordRef.js
export default {
  name: 'keywordRef',
  type: 'object',
  title: 'Keyword',
  fields: [
    {
      name: 'keyword',
      type: 'reference',
      to: [
        {
          type: 'keyword'
        }
      ]
    }
  ],
  preview: {
    select: {
      title: 'keyword.title'
    }
  }
}
I actually have defined them as a document, so is the
preview
element the bit that will make this sort of appear as text instead of this large element? Trying to understand the terms and thought process - still a little hard to know what I need to look for! 😄
If they’re now defined as documents, i.e. they already exist when a Studio user wants to insert them as a keyword, then you could go with the above approach to simply let them set a reference to the document. That reference is inline and is an object, but what it refers to can be a document.
If you mean for the Studio user to write a new, non-existing keyword right there in the editor, then this approach won’t work I’m afraid.

In terms of the preview, it lets you define exactly what info is shown inline in the editor - in this case, the referred-to keyword document’s title. Hope that helps clear things up a bit?
🙂
so looks like nesting another
of
inside the block array gets me a lot closer, and now it certainly looks better
I’ll reference a document I think, we’ll reuse these keywords elsewhere most likely
That makes sense. It can be a little bit of a hassle to define these separately, especially at the start when there are not many keywords yet perhaps. But it makes things more tidy and query-able afterwards. Hopefully we can add a ‘create reference in place’ functionality along the way 😉
ok, so to clarify, there’s not a way to “create” a new keyword while in the block editor, I would just need to create the doc first?
Not if keyword is a
document
type, not yet at least. It’s a much requested feature to be able to create these ‘on the fly’ wherever you would reference them.
If
keyword is an
object
type then, yes, you can create these directly in the editor (but they won’t be reusable in other places).
ok that’s fine with me, good to know that’s the limitation. Last thing, for the editor button, how do I change the “Reference to Keyword” text?
You can customise this by adding something like
title: 'Keyword'
to the reference object.
keywords.js

import { FaBolt } from 'react-icons/fa';

export default {
  name: 'keyword',
  title: 'Keyword',
  type: 'document',
  icon: FaBolt,
  preview: {
    select: {
      title: 'keyword',
    },
  },
  fields: [
    {
      name: 'keyword',
      title: 'Keyword',
      type: 'string',
    },
    {
      name: 'description',
      title: 'Description',
      type: 'array',
      of: [
        {
          title: 'Block',
          type: 'block',
          styles: [{ title: 'Normal', value: 'normal' }],
          lists: [],
        },
      ],
    },
  ],
};
blockContent.js
import React from 'react';

export default {
  title: 'Block Content',
  name: 'blockContent',
  type: 'array',
  of: [
    {
      title: 'Block',
      type: 'block',
      // Styles let you set what your user can mark up blocks with. These
      // corrensponds with HTML tags, but you can set any title or value
      // you want and decide how you want to deal with it where you want to
      // use your content.
      styles: [
        { title: 'Normal', value: 'normal' },
        { title: 'H1', value: 'h1' },
        { title: 'H2', value: 'h2' },
        { title: 'H3', value: 'h3' },
        { title: 'H4', value: 'h4' },
        { title: 'H5', value: 'h5' },
        { title: 'H6', value: 'h6' },
        { title: 'Quote', value: 'blockquote' },
      ],
      lists: [
        { title: 'Bullet', value: 'bullet' },
        { title: 'Numbered', value: 'number' },
      ],
      // Marks let you mark up inline text in the block editor.
      marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          { title: 'Strong', value: 'strong' },
          { title: 'Emphasis', value: 'em' },
          { title: 'Underline', value: 'underline' },
          // { title: 'Keyword', value: 'keyword', icon: FaBolt, render: keyword },
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          // { type: 'twitter' },
          // { type: 'externalLink' },
          // { type: 'internalLink' },
          // { type: 'keyword' },
        ],
      },
      of: [
        {
          type: 'reference',
          to: [
            {
              type: 'keyword',
            },
          ],
        },
      ],
    },
    // You can add additional types here. Note that you can't use
    // primitive types such as 'string' and 'number' in the same array
    // as a block type.
    {
      type: 'figure',
    },
  ],
};
I still have that long description for the keyword item in the editor
And if you change it to this instead?
...
      of: [
        {
          type: 'reference',
          title: 'Keyword',
          to: [
            {
              type: 'keyword',
            },
          ],
        },
      ],
...
A more flexible way of achieving the above is creating an additional type as explained earlier in the thread:
export default {
  name: 'keywordRef',
  type: 'object',
  title: 'Keyword',
  fields: [
    {
      name: 'keyword',
      type: 'reference',
      to: [
        {
          type: 'keyword'
        }
      ]
    }
  ],
  preview: {
    select: {
      title: 'keyword.keyword'
    }
  }
}
And including it as
of: [{type: 'keywordRef'}]
instead. That way, you can have some different settings (previews in particular) for your keyword reference in block content, and for your keyword document list elsewhere in the Studio. Hope it’s a bit clear what I mean with that 😉
great that’s perfect! Making a bit more sense now how I can customize at each layer, but it’s a lot to take in.. thanks!
Agreed, there’s plenty of ground to cover. Hang in there 🙂

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?