Skip to content

React

Terminal window
npm i @portabletext/react

Pass your Portable Text value to the PortableText component. The library handles blocks, marks, lists, and inline content with sensible defaults.

import {PortableText} from '@portabletext/react'
export default function MyComponent({value}) {
return <PortableText value={value} />
}

The value prop accepts a single block object or an array of block objects.

Override the default rendering by passing a components prop. Each key in the object maps to a part of the Portable Text structure.

Marks are inline annotations: bold, italic, links, and any custom annotations you define. Override them under marks.

This example adds safe rel attributes to external links:

const components = {
marks: {
link: ({children, value}) => {
const rel = !value.href.startsWith('/')
? 'noreferrer noopener'
: undefined
return (
<a href={value.href} rel={rel}>
{children}
</a>
)
},
},
}

Pass the components object to PortableText:

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

Custom block type (image with @sanity/image-url)

Section titled “Custom block type (image with @sanity/image-url)”

Custom block types (objects embedded in your content) are handled under types. Each component receives the block’s value and an isInline boolean indicating whether it appears inline or as a standalone block.

import {getImageDimensions} from '@sanity/asset-utils'
import {createImageUrlBuilder} from '@sanity/image-url'
// Initialize with your Sanity project config
const builder = createImageUrlBuilder({
projectId: 'yourProjectId',
dataset: 'production',
})
const urlFor = (source) => builder.image(source)
const SampleImageComponent = ({value, isInline}) => {
const {width, height} = getImageDimensions(value)
return (
<img
src={urlFor(value)
.width(isInline ? 100 : 800)
.fit('max')
.auto('format')
.url()}
alt={value.alt || ' '}
loading="lazy"
style={{
display: isInline ? 'inline-block' : 'block',
aspectRatio: width / height,
}}
/>
)
}

Register it under the type name you used in your schema:

const components = {
types: {
image: SampleImageComponent,
},
}

You can define types, marks, and block styles in a single components object:

const components = {
types: {
image: ({value}) => <img src={value.imageUrl} />,
callToAction: ({value, isInline}) =>
isInline ? (
<a href={value.url}>{value.text}</a>
) : (
<div className="callToAction">{value.text}</div>
),
},
marks: {
link: ({children, value}) => {
const rel = !value.href.startsWith('/')
? 'noreferrer noopener'
: undefined
return (
<a href={value.href} rel={rel}>
{children}
</a>
)
},
},
block: {
h1: ({children}) => <h1 className="text-2xl">{children}</h1>,
blockquote: ({children}) => (
<blockquote className="border-l-purple-500">{children}</blockquote>
),
},
}

Import PortableTextComponents to type your components object. This gives you autocomplete on component keys and typed props for each renderer.

import {PortableText, PortableTextComponents} from '@portabletext/react'
const components: PortableTextComponents = {
marks: {
link: ({value, children}) => {
const target = (value?.href || '').startsWith('http')
? '_blank'
: undefined
return (
<a
href={value?.href}
target={target}
rel={target === '_blank' ? 'noindex nofollow' : undefined}
>
{children}
</a>
)
},
},
block: {
normal: ({children}) => <p className="text-base">{children}</p>,
h1: ({children}) => <h1 className="text-2xl">{children}</h1>,
},
}
export default function Article({value}) {
return <PortableText value={value} components={components} />
}

The components prop accepts the following keys. Any key you omit falls back to the default renderer.

KeyWhat it renders
typesCustom block types and inline object types. Keyed by _type value.
marksInline annotations (links, custom decorators). Keyed by mark type name.
blockStandard text blocks. Keyed by style value (normal, h1, h2, blockquote, etc.).
listList wrappers. Keyed by list type (bullet, number).
listItemIndividual list items. Keyed by list type.
hardBreakLine breaks within a block (rendered as <br /> by default).
unknownMarkFallback for mark types with no matching component.
unknownTypeFallback for block types with no matching component.
unknownBlockStyleFallback for block styles with no matching component.
unknownListFallback for list types with no matching component.
unknownListItemFallback for list item types with no matching component.

The unknown* keys are useful for debugging: render a visible warning so you can spot unhandled types during development.

Full API reference, changelog, and additional examples: @portabletext/react on GitHub.