React
Install
Section titled “Install”npm i @portabletext/reactpnpm add @portabletext/reactyarn add @portabletext/reactBasic usage
Section titled “Basic usage”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.
Custom components
Section titled “Custom components”Override the default rendering by passing a components prop. Each key in the object maps to a part of the Portable Text structure.
Custom link annotation
Section titled “Custom link annotation”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 configconst 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, },}Combined example
Section titled “Combined example”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> ), },}TypeScript
Section titled “TypeScript”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} />}Available component keys
Section titled “Available component keys”The components prop accepts the following keys. Any key you omit falls back to the default renderer.
| Key | What it renders |
|---|---|
types | Custom block types and inline object types. Keyed by _type value. |
marks | Inline annotations (links, custom decorators). Keyed by mark type name. |
block | Standard text blocks. Keyed by style value (normal, h1, h2, blockquote, etc.). |
list | List wrappers. Keyed by list type (bullet, number). |
listItem | Individual list items. Keyed by list type. |
hardBreak | Line breaks within a block (rendered as <br /> by default). |
unknownMark | Fallback for mark types with no matching component. |
unknownType | Fallback for block types with no matching component. |
unknownBlockStyle | Fallback for block styles with no matching component. |
unknownList | Fallback for list types with no matching component. |
unknownListItem | Fallback 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.
Further reading
Section titled “Further reading”Full API reference, changelog, and additional examples: @portabletext/react on GitHub.