Can I wrap multiple <li> elements in a div using Sanity portable text marks?

25 replies
Last updated: May 27, 2022
hi ! i'd like to know if i can wrap my
<li>
elements in a div.current schema :
marks: {

decorators: [

{ title: "strong", value: "strong" },

{ title: "em", value: "em" },

{ title: "strike", value: "strike" },

{ title: "underline", value: "underline" },

{ title: "blue", value: "blue" },

{ title: "green", value: "green" },

{ title: "animation", value: "animation" },

{ title: "muted", value: "muted" },

{ title: "div", value: "div" },
But when i do something like that it just wrap my element in the li not the 2 li elements i want.
I just want the last li elements to be wrapped inside the div
AI Update

I understand what you're trying to do, but unfortunately Portable Text decorators (marks) can't wrap multiple list items (<li>) together because decorators work at the inline/span level, not at the block structure level.

When you apply a decorator like "div" to text within a list, it only wraps the text content inside the <li>, not the <li> elements themselves. This is by design—marks are meant for inline formatting (like bold, italic, links), not for changing block-level structure.

What You Can Do Instead

Here are a few approaches to achieve what you want:

1. Use Custom List Rendering

You can customize how lists are rendered using @portabletext/react serializers:

const components = {
  list: {
    bullet: ({children}) => (
      <ul>
        <div className="wrapper">
          {children}
        </div>
      </ul>
    )
  }
}

<PortableText value={content} components={components} />

However, this wraps all list items in that list, not just specific ones.

2. Create a Custom Block Type

Instead of using the built-in list feature, create a custom block type in your schema:

{
  name: 'wrappedList',
  type: 'object',
  fields: [
    {
      name: 'items',
      type: 'array',
      of: [{type: 'block'}]
    },
    {
      name: 'wrapInDiv',
      type: 'boolean',
      title: 'Wrap in div'
    }
  ]
}

Then render it with custom logic in your frontend to conditionally wrap specific items based on your needs.

3. Use CSS Instead

If this is purely for styling purposes, you might be able to achieve the visual effect with CSS using pseudo-elements, CSS Grid, or Flexbox on the <ul> without changing the HTML structure.

4. Custom listItem Serializer

You could also add logic to the listItem serializer to detect certain conditions and wrap accordingly, though this gets complex:

const components = {
  listItem: ({children, value, index}) => {
    // Custom logic to determine if this should be wrapped
    return <li>{children}</li>
  }
}

The key limitation is that marks/decorators operate on text spans, not block structures, so they can't restructure your list hierarchy. You'll need to either customize the list rendering as a whole or create a custom block type for more complex list structures where you need selective wrapping.

Show original thread
25 replies
Wrapping a
<li>
element in a
<div>
would yield invalid HTML and break list semantics.
ok so i tried to put an h2 and an h1 in a div but it doesnt let me
do i have to put the div as a decorator ?
<p class="text-2xl p-2"><div class="text-2xl p-2">Design</div></p>

<p class="text-2xl p-2"><div class="text-2xl p-2">UX/UI</div></p>
my code looks like that
<div><p class="text-2xl p-2">Design</p>

<p class="text-2xl p-2"><p class="text-2xl p-2">UX/UI</p></div>
while i want it to look like that
here s my portable text code
const portraitPortableTextComponents: any = {

block: {

normal: ({ children }) => <p _className_="text-2xl p-2">{children}</p>,

h3: ({ children }) => <h3 _className_="font-bold">{children}</h3>,

},

marks: {

div: ({ children }) => <div _className_="text-2xl p-2">{children}</div>,

},

}
tldr i want to wrap my content in a div
tldr i want to wrap my content in a div in a rich text block
I’m a little confused as to why? The rich text editor is not an HTML editor. It gives structure, not markup. Ultimately, you map these nodes to whatever you want on your frontend. Or am I missing something?
it was just to have a line break but after a seond thought i might me able to do it another way like "going to the next line" :X
in my editor
its true that i was hoping to replace all my tags with predefined ones in my editor but that my not be the right way to do things
I would generally advise against trying to make the editor look like the frontend version of the content. The editor is suppose to convey content and structure, but not design per se. 🙂
alright you must be right. I'm new to the cms things to be honest and i 'm trying to find my way on my own.
tahnk you for your advices i will rethink my process
It takes a bit of time to get the hang of it, but I think a healthy way to think about the portable text editor is to think about structure really. Similar to Markdown in a way.
You or your editors need to be able to say “this is a heading, this is a list, this is a map widget with these options, this is an image with this alt text, this is a 2 columns layout with this content.” How it will look (beyond the options you defined like layout or things like this) is the responsibility of the frontend. And markup is definitely on the frontend side as well.
🙂
alright 🙂
thank you for your time
no need to say ill be back
haha
Haha, take care. ❤️
thanks you too

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?