Question about Portable Text editor and ul/ol lists in Sanity CMS

8 replies
Last updated: Sep 1, 2021
Hi everyone and thanks for a very sane CMS solution! I have a question about Portable text editor and showing of ol/ul lists.
AI Update

Glad to hear you're enjoying Sanity! When it comes to showing ordered (ol) and unordered (ul) lists in the Portable Text editor, you have full control over the list configuration in your schema.

Default List Configuration

By default, the Portable Text editor includes both bullet (unordered) and numbered (ordered) lists. They appear as toolbar buttons in the editor:

export default {
  name: 'content',
  type: 'array',
  of: [
    {
      type: 'block',
      // Lists are enabled by default
    }
  ]
}

Customizing Lists

You can explicitly configure which lists are available using the lists property:

export default {
  name: 'content',
  type: 'array',
  of: [
    {
      type: 'block',
      lists: [
        {title: 'Bullet', value: 'bullet'},
        {title: 'Numbered', value: 'number'}
      ]
    }
  ]
}

To disable lists entirely, just pass an empty array:

lists: [] // no lists

To enable only one type, just include that one:

lists: [
  {title: 'Bullet', value: 'bullet'}
] // only unordered lists

You can also customize the titles that appear in the editor - for example, {title: 'Prioritized', value: 'number'} works just fine!

Rendering Lists

When rendering your Portable Text content on the frontend using @portabletext/react, lists are automatically rendered as <ul> and <ol> elements. You can customize this with custom serializers if needed.

More details can be found in the Configuring lists for text blocks section of the documentation.

This is how my editor looks. I’d like to have it ul/ol buttons as well.
I’ve checked the documentation and this is the config I’m using:

/**
 * This is the schema definition for the rich text fields used for
 * for this blog studio. When you import it in schemas.js it can be
 * reused in other parts of the studio with:
 *  {
 *    name: 'someName',
 *    title: 'Some title',
 *    type: 'blockContent'
 *  }
 */
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
      // correspond 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: "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" }
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            title: "URL",
            name: "link",
            type: "object",
            fields: [
              {
                title: "URL",
                name: "href",
                type: "url"
              }
            ]
          }
        ]
      }
    },
    // 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: "image",
      options: { hotspot: true }
    }
  ]
};

I’m probably missing some config gotcha. 🙂 Would you please point me in the right direction? Thanks in advance!
Hi
user A
, thanks for a fast reply! In the end it was my mistake - because of i18n I use two different schemas for Portable text. The code I’ve showed you was correct, the other one (on the screenshot) had duplicated properties.
Hi
user A
, thanks for a fast reply! In the end it was my mistake - because of i18n I use two different schemas for Portable text. The code I’ve showed you was correct, the other one (on the screenshot) had duplicated properties.
On unrelated note - I’m quite amazed how nice and fast your community support is! So keep up the good work please, you make developer’s lives easier and much more pleasant. 🙂
Hi Petr. I’m glad to hear you got that worked out. Also, thank you for the kind words. We’re really happy to hear that. Thank you!
Hi Petr. I’m glad to hear you got that worked out. Also, thank you for the kind words. We’re really happy to hear that. Thank you!

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?