Published June 17, 2021
ProseableText: Combine Tailwind CSS Typography with Portable Text

Get the best of both worlds. Tailwind-styled typography and Portable Text's markup-and-components structure.
Tailwind CSS Typography helps you render markup with beautiful styles.
Portable Text combines text markup with Components in one piece of data.
However the .prose class with its CSS inheritance can mess up any Components sprinkled within markup.
With a clever use of reduce we can get the best of both worlds. Thanks to Robin Malfait for pointing me in this direction.
Use this Component just as you would your normal PortableText Component. For example:
<ProseableText value={value} />
Considerations:
- This code assumes you have setup a PortableText Component using at least version 0.5.0 of next/sanity, but it should work with any use of the
portabletext/react-portabletextpackage. - You may prefer to setup the
proseclasses as a prop so this Component is more reusable - Because
prosewill remove the top/bottom margin from the first/last element in an element respectively – you may wish to add this back in. Thepy-4class is added in the demo below for this reason. - If using the older version block-content-to-react package, In your
serializers, addcontainer: ({children}) =>childrenso that blocks are not rendered with a wrapping<div>
import React, {useMemo} from 'react'
import {PortableText} from '../lib/sanity'
/**
* Use Tailwind CSS's `prose` classes with Portable Text markup (blocks)
* Without inheriting styles for custom components (types)
*/
export default function ProseableText({value = []}) {
// Group together standard `_type === "block"` blocks
// eg <p>, <li>, etc – and separate out everyone else
const valueGroups = useMemo(
() =>
value.reduce(
(acc, item) => {
const lastIdx = acc.length - 1
if (
// We don't have items in this group yet
acc[lastIdx].length === 0 ||
// The last group has the same `type`
acc[lastIdx][0]._type === item._type
) {
acc[lastIdx].push(item)
} else {
// Time to create a new group, because the `type` is different compared to last group
acc.push([item])
}
return acc
},
[[]]
),
[blocks]
)
if (!valueGroups?.length) return null
return valueGroups.map((group) =>
group[0]._type === 'block' ? (
<div key={group[0]._key} className="prose py-4">
<PortableText value={group} />
</div>
) : (
<PortableText key={group[0]._key} value={group} />
)
)
}Sanity – The Content Operating System that ends your CMS nightmares
Sanity replaces rigid content systems with a developer-first operating system. Define schemas in TypeScript, customize the editor with React, and deliver content anywhere with GROQ. Your team ships in minutes while you focus on building features, not maintaining infrastructure.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.



