### Install @portabletext/react
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Install the library using npm. This is the first step to using @portabletext/react in your project.
```bash
npm install --save @portabletext/react
```
--------------------------------
### Create Linkable Headers using Portable Text and Plain Text Conversion
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Generate unique IDs for headers by combining `toPlainText()` with a slugification library. This example demonstrates how to create linkable `h2` elements by converting Portable Text block content to a slug for the `id` attribute.
```tsx
import {PortableText, toPlainText, PortableTextComponents}
import slugify from 'slugify'
const LinkableHeader = ({children, value}) => {
// `value` is the single Portable Text block of this header
const slug = slugify(toPlainText(value))
return
{children}
}
const components = {
block: {
h2: LinkableHeader,
},
} satisfies PortableTextComponents
```
--------------------------------
### Implement Custom Image Component for react-portabletext
Source: https://github.com/portabletext/react-portabletext/blob/main/MIGRATING.md
Provide a custom image component to render images when using react-portabletext. This example shows a barebones lazy-loaded image component using `@sanity/image-url` and `@sanity/asset-utils`.
```jsx
import urlBuilder from '@sanity/image-url'
import {getImageDimensions} from '@sanity/asset-utils'
// Barebones lazy-loaded image component
const SampleImageComponent = ({value}) => {
const {width, height} = getImageDimensions(value)
return (
)
}
// You'll now need to define your own image component
```
--------------------------------
### Integrate CustomPortableText Component into a Page
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Fetch Portable Text content using GROQ and render it using the `CustomPortableText` component. Ensure the fetched content is an array before passing it to the component.
```tsx
import {createClient} from '@sanity/client'
import {defineQuery} from 'groq'
const client = createClient(...)
export default async function Page({slug}: {slug: string}) {
const postQuery = defineQuery(`*[_type == "post" && slug.current == $slug][0]{title,content}`)
const data = await client.fetch(postQuery, {slug})
if (!data) return notFound()
return (
{data.title}
{Array.isArray(data.content) && }
)
}
```
--------------------------------
### Import PortableText component
Source: https://github.com/portabletext/react-portabletext/blob/main/MIGRATING.md
Switch from default import of `BlockContent` to named import of `PortableText`.
```jsx
// From:
import BlockContent from '@sanity/block-content-to-react'
// ✅ To:
// Not the default export anymore
import { PortableText } from '@portabletext/react'
```
--------------------------------
### Customizing Marks (Em, Link, Highlight)
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Override default rendering for text marks like 'em', 'link', and a custom 'highlight' mark. The 'link' mark demonstrates handling external URLs.
```tsx
import { PortableText, type PortableTextComponents } from '@portabletext/react'
const components: PortableTextComponents = {
marks: {
// Decorator override
em: ({ children }) => (
{children}
),
// Annotation: custom link with external target detection
link: ({ value, children }) => {
const isExternal = (value?.href || '').startsWith('http')
return (
{children}
)
},
// Custom annotation mark for highlighted text
highlight: ({ value, children }) => (
{children}
),
},
}
export function RichText({ value }) {
return
}
```
--------------------------------
### Convert Portable Text to Plain Text with `toPlainText()`
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Utility function to convert Portable Text blocks into a plain string. Useful for meta tags, slugs, or search indexing where formatted output is not supported.
```tsx
import { PortableText, toPlainText, type PortableTextComponents } from '@portabletext/react'
import slugify from 'slugify'
// Generate OG description from rich text
export function PageMeta({ content }) {
const description = toPlainText(content).slice(0, 155)
return
}
```
```tsx
import { PortableText, toPlainText, type PortableTextComponents } from '@portabletext/react'
import slugify from 'slugify'
// Generate anchor IDs for h2 blocks
const components: PortableTextComponents = {
block: {
h2: ({ value, children }) => {
const id = slugify(toPlainText(value), { lower: true, strict: true })
return {children}
},
},
}
export function Article({ content }) {
return
}
```
--------------------------------
### Create a Reusable CustomPortableText Component
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Define a `CustomPortableText` component that accepts Portable Text values and uses `InferStrictComponents` to ensure all schema types are handled. This component is ideal for consistent rendering across your application.
```tsx
import type { SanityQueries} from '@sanity/client'
import {createImageUrlBuilder} from '@sanity/image-url'
import {
PortableText,
type InferStrictComponents,
type InferValue,
} from '@portabletext/react'
const builder = createImageUrlBuilder(...)
// Array value type for every Portable Text item shape across all registered queries.
type PortableTextValue = InferValue
export function CustomPortableText({value}: {value: PortableTextValue}) {
const components = {
types: {
// `value` is fully typed from the inferred image variant.
image: ({value}) =>
,
},
// Add `types`, `marks`, `block`, `list` etc. handlers as your schema requires.
} satisfies InferStrictComponents
// ^ TypeScript errors when the schema gains a custom type, block, mark, or list
// style without a matching handler defined here.
return
}
```
--------------------------------
### toPlainText() - Strip Formatting
Source: https://context7.com/portabletext/react-portabletext/llms.txt
A utility function to strip all formatting from Portable Text and return plain text.
```APIDOC
## toPlainText()
### Description
Strips all formatting and structural elements from Portable Text content, returning a plain text string.
### Usage
```typescript
import { toPlainText } from '@portabletext/react'
const blocks = [
// ... Portable Text blocks
]
const plainText = toPlainText(blocks)
console.log(plainText) // Outputs the text content without any formatting
```
### Parameters
- **value** (Array | PortableTextBlock): The Portable Text content to process.
```
--------------------------------
### Convert Portable Text to Plain Text with React Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Utilize the `toPlainText()` function to convert Portable Text blocks into a plain text string. This is useful for scenarios like generating meta descriptions or creating accessible text alternatives where formatting is not required or supported.
```tsx
import {toPlainText}
const MetaDescription = (myPortableTextData) => {
return
}
```
--------------------------------
### Basic PortableText Usage in React
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Render Portable Text content by passing an array of blocks to the PortableText component. Optionally, provide custom components for rendering.
```jsx
import {PortableText} from '@portabletext/react'
```
--------------------------------
### `toPlainText()` — strip all formatting
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Utility function that converts one or more Portable Text blocks into a plain string. Useful for Open Graph meta tags, slug generation, text search indexing, or anywhere formatted output is not supported.
```APIDOC
## `toPlainText()` — strip all formatting
### Description
Utility function that converts one or more Portable Text blocks into a plain string. Useful for Open Graph meta tags, slug generation, text search indexing, or anywhere formatted output is not supported.
### Usage
**Generate OG description from rich text:**
```tsx
import { PortableText, toPlainText, type PortableTextComponents } from '@portabletext/react'
import slugify from 'slugify'
export function PageMeta({ content }) {
const description = toPlainText(content).slice(0, 155)
return
}
```
**Generate anchor IDs for h2 blocks:**
```tsx
import { PortableText, toPlainText, type PortableTextComponents } from '@portabletext/react'
import slugify from 'slugify'
const components: PortableTextComponents = {
block: {
h2: ({ value, children }) => {
const id = slugify(toPlainText(value), { lower: true, strict: true })
return {children}
},
},
}
export function Article({ content }) {
return
}
```
```
--------------------------------
### Advanced Portable Text Typing with Generics
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Create custom, narrowed Portable Text types by specifying generics for marks, inline blocks, styles, and list types. This allows for precise typing of complex Portable Text structures.
```ts
import {PortableTextBlock, PortableTextMarkDefinition, PortableTextSpan} from '@portabletext/types'
// MARKS
interface FirstMark extends PortableTextMarkDefinition {
_type: 'firstMark'
// ...other fields
}
interface SecondMark extends PortableTextMarkDefinition {
_type: 'secondMark'
// ...other fields
}
type CustomMarks = FirstMark | SecondMark
// INLINE BLOCKS
interface MyInlineBlock {
_type: 'myInlineBlock'
// ...other fields
}
type InlineBlocks = PortableTextSpan | MyInlineBlock
// STYLES
type TextStyles = 'normal' | 'h1' | 'myCustomStyle'
// LISTS
type ListStyles = 'bullet' | 'myCustomList'
// CUSTOM PORTABLE TEXT BLOCK
// Putting it all together by specifying generics
// all of these are valid:
// type CustomPortableTextBlock = PortableTextBlock
// type CustomPortableTextBlock = PortableTextBlock
// type CustomPortableTextBlock = PortableTextBlock
type CustomPortableTextBlock = PortableTextBlock
// Other BLOCKS that can appear inbetween text
interface MyCustomBlock {
_type: 'myCustomBlock'
// ...other fields
}
// TYPE FOR PORTABLE TEXT FIELD ITEMS
type PortableTextFieldType = CustomPortableTextBlock | MyCustomBlock
// Using it in your document type
interface MyDocumentType {
portableTextField: PortableTextFieldType[]
}
```
--------------------------------
### Basic PortableText Usage
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Minimal usage of the `` component with default rendering for block types.
```tsx
import { PortableText } from '@portabletext/react'
// Minimal usage — uses built-in defaults for all node types
const blocks = [
{
_type: 'block',
_key: 'a1',
style: 'normal',
children: [{ _type: 'span', _key: 's1', text: 'Hello, world!', marks: [] }],
markDefs: [],
},
{
_type: 'block',
_key: 'a2',
style: 'h2',
children: [{ _type: 'span', _key: 's2', text: 'A heading', marks: [] }],
markDefs: [],
},
]
export function Article() {
return (
{/* Renders: Hello, world!
A heading
*/}
)
}
```
--------------------------------
### InferComponents for Flexible Component Handling
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Use `InferComponents` when you need flexibility, such as during incremental migration or when dealing with frequently changing schemas. It allows optional handlers and permits extra handlers for types not present in the schema.
```tsx
import {PortableText, type InferComponents} from '@portabletext/react'
const components = {
types: {
image: ({value}) =>
,
// Optional: legacy types not in the current schema are allowed.
},
} satisfies InferComponents
```
--------------------------------
### Customizing PortableText Components
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Define custom components for 'types' and 'marks' to control how specific Portable Text elements are rendered. Ensure referential identity for the components object for performance.
```jsx
const myPortableTextComponents = {
types: {
image: ({value}) =>
,
callToAction: ({value, isInline}) =>
isInline ? (
{value.text}
) : (
{value.text}
),
},
marks: {
link: ({children, value}) => {
const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined
return (
{children}
)
},
},
}
const YourComponent = (props) => {
return
}
```
--------------------------------
### - Main Rendering Component
Source: https://context7.com/portabletext/react-portabletext/llms.txt
The primary export for rendering Portable Text. It accepts a `value` prop (Portable Text blocks), an optional `components` map for customization, and an optional `onMissingComponent` handler. It renders a React fragment.
```APIDOC
## Component
### Description
Renders Portable Text blocks into React elements. It uses built-in defaults for standard node types and allows for extensive customization through the `components` prop.
### Usage
```tsx
import { PortableText } from '@portabletext/react'
const blocks = [
{
_type: 'block',
_key: 'a1',
style: 'normal',
children: [{ _type: 'span', _key: 's1', text: 'Hello, world!', marks: [] }],
markDefs: [],
},
{
_type: 'block',
_key: 'a2',
style: 'h2',
children: [{ _type: 'span', _key: 's2', text: 'A heading', marks: [] }],
markDefs: [],
},
]
export function Article() {
return (
{/* Renders: Hello, world!
A heading
*/}
)
}
```
### Props
- **value** (Array | PortableTextBlock): The Portable Text content to render.
- **components** (PortableTextComponents) - Optional: An object to override default rendering components for different Portable Text types and marks.
- **onMissingComponent** (function) - Optional: A handler function called when a component for a specific type or mark is not found.
```
--------------------------------
### `onMissingComponent` — missing component handler
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Prop on `` that intercepts warnings for unregistered node types. Defaults to `console.warn`. Pass `false` to silence all warnings, or a function to route them to your own error tracking system.
```APIDOC
## `onMissingComponent` — missing component handler
### Description
Prop on `` that intercepts warnings for unregistered node types. Defaults to `console.warn`. Pass `false` to silence all warnings, or a function to route them to your own error tracking system.
### Usage
**Silence all warnings:**
```tsx
import { PortableText } from '@portabletext/react'
```
**Send to an error tracker:**
```tsx
import { PortableText } from '@portabletext/react'
{
// options.type — e.g. 'myCustomBlock'
// options.nodeType — 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle'
Sentry.captureMessage(message, {
level: 'warning',
extra: { type: options.type, nodeType: options.nodeType },
})
}}
/>
```
```
--------------------------------
### Handle Missing Components with `onMissingComponent`
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Intercept warnings for unregistered node types in PortableText. Pass `false` to silence warnings or a function to route them to an error tracking system.
```tsx
import { PortableText } from '@portabletext/react'
// Silence all warnings
```
```tsx
import { PortableText } from '@portabletext/react'
// Send to an error tracker
{
// options.type — e.g. 'myCustomBlock'
// options.nodeType — 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle'
Sentry.captureMessage(message, {
level: 'warning',
extra: { type: options.type, nodeType: options.nodeType },
})
}}
/>
```
--------------------------------
### Custom List Components for Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Customizes the rendering of bulleted and numbered lists, and a custom 'checkmarks' list type. Applies margin classes for spacing.
```jsx
const components = {
list: {
// Ex. 1: customizing common list types
bullet: ({children}) => ,
number: ({children}) => {children}
,
// Ex. 2: rendering custom lists
checkmarks: ({children}) => {children}
,
},
}
```
--------------------------------
### Custom Block Components for Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Customizes rendering for common block styles like 'normal', 'h1', and 'blockquote', as well as a custom 'customHeading' style. Uses Tailwind CSS classes for styling.
```tsx
import {PortableText, PortableTextReactComponents}
// `components` object you'll pass to PortableText
const components = {
block: {
// Ex. 1: customizing common block types
normal: ({children}) => {children}
,
h1: ({children}) => {children}
,
blockquote: ({children}) => {children}
,
// Ex. 2: rendering custom styles
customHeading: ({children}) => (
{children}
),
},
} satisfies Partial
```
--------------------------------
### InferStrictComponents for Production-Grade Renderers
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Employ `InferStrictComponents` to enforce that your schema and renderer are in sync. It requires handlers for all inferred types and rejects handlers for keys not present in the schema, failing TypeScript compilation if there are discrepancies.
```tsx
import {PortableText, type InferStrictComponents} from '@portabletext/react'
const components = {
types: {
image: ({value}) =>
,
// Omit `image` and TypeScript errors.
// Add `legacyEmbed` and TypeScript errors.
},
} satisfies InferStrictComponents
```
--------------------------------
### components.marks - Decorator and Annotation Marks
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Customize the rendering of inline text marks, including decorators (like bold or italic) and annotations (like links).
```APIDOC
## components.marks
### Description
Defines custom React components for rendering inline text marks. Decorator marks receive `children` and `markType`, while annotation marks also receive a `value` object containing their definition data.
### Usage
```tsx
import { PortableText, type PortableTextComponents } from '@portabletext/react'
const components: PortableTextComponents = {
marks: {
// Decorator override
em: ({ children }) => (
{children}
),
// Annotation: custom link with external target detection
link: ({ value, children }) => {
const isExternal = (value?.href || '').startsWith('http')
return (
{children}
)
},
// Custom annotation mark for highlighted text
highlight: ({ value, children }) => (
{children}
),
},
}
export function RichText({ value }) {
return
}
```
### Component Props
- **children**: The content within the mark.
- **value** (object) - For annotation marks: Contains the definition data for the mark (e.g., `href` for links).
```
--------------------------------
### InferComponents vs InferStrictComponents for Component Maps
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Use `InferComponents` for forgiving component maps where optional handlers are allowed. Use `InferStrictComponents` for strict typing, ensuring all custom handlers are defined and unknown handlers are rejected.
```tsx
import { PortableText, type InferComponents, type InferStrictComponents } from '@portabletext/react'
import { createClient } from '@sanity/client'
import { defineQuery } from 'groq'
const client = createClient({ projectId: 'abc123', dataset: 'production', apiVersion: '2024-01-01', useCdn: true })
// --- Forgiving: InferComponents ---
export default async function BlogPage({ slug }: { slug: string }) {
const query = defineQuery(`*[_type == "post" && slug.current == $slug][0]{title,content}`)
const data = await client.fetch(query, { slug })
const components = {
block: {
h2: ({ children }) => {children}
,
// Extra handlers for types not in this query are allowed
},
} satisfies InferComponents
return (
{data?.title}
{Array.isArray(data?.content) && }
)
}
// --- Strict: InferStrictComponents ---
// Narrowed to specific query result types to keep the union tight
import type { PostQueryResult, AuthorQueryResult } from './sanity.types'
type Value = InferValue
export function StrictPortableText({ value }: { value: Value }) {
const components = {
types: {
image: ({ value }) =>
,
// Omit `image` → TypeScript error. Add `legacyEmbed` → TypeScript error.
},
} satisfies InferStrictComponents
return
}
```
--------------------------------
### Custom Mark Components for Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Defines custom renderers for emphasis (em) and link annotations. The `link` component handles external URLs by opening them in a new tab.
```tsx
// `components` object you'll pass to PortableText w/ optional TS definition
import {PortableTextComponents}
const components = {
marks: {
// Ex. 1: custom renderer for the em / italics decorator
em: ({children}) => {children},
// Ex. 2: rendering a custom `link` annotation
link: ({value, children}) => {
const target = (value?.href || '').startsWith('http') ? '_blank' : undefined
return (
{children}
)
},
},
} satisfies PortableTextComponents
```
--------------------------------
### Basic Portable Text Typing with @portabletext/types
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Use `PortableTextBlock` without generics for loosely typed Portable Text data when not using Sanity TypeGen. This provides default handling for basic block structures.
```ts
import {PortableTextBlock} from '@portabletext/types'
interface MySanityDocument {
portableTextField: (PortableTextBlock | SomeBlockType)[]
}
```
--------------------------------
### Custom Image Component for Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Renders a custom image type, handling inline vs. block display and lazy loading. Requires `@sanity/image-url` and `@sanity/asset-utils` for image processing and dimension retrieval.
```jsx
import {PortableText} from '@portabletext/react'
import urlBuilder from '@sanity/image-url'
import {getImageDimensions} from '@sanity/asset-utils'
// Barebones lazy-loaded image component
const SampleImageComponent = ({value, isInline}) => {
const {width, height} = getImageDimensions(value)
return (
)
}
const components = {
types: {
image: SampleImageComponent,
// Any other custom types you have in your content
// Examples: mapLocation, contactForm, code, featuredProjects, latestNews, etc.
},
}
const YourComponent = (props) => {
return
}
```
--------------------------------
### Manually Typing PortableTextBlock with Generics
Source: https://context7.com/portabletext/react-portabletext/llms.txt
When not using Sanity TypeGen, use `PortableTextBlock` generics to manually define mark definitions, inline block shapes, block styles, and list item types for strict typing.
```ts
import {
PortableTextBlock,
PortableTextMarkDefinition,
PortableTextSpan,
} from '@portabletext/types'
// --- Mark definitions ---
interface LinkMark extends PortableTextMarkDefinition {
_type: 'link'
href: string
blank?: boolean
}
interface HighlightMark extends PortableTextMarkDefinition {
_type: 'highlight'
color: string
}
type CustomMarks = LinkMark | HighlightMark
// --- Inline blocks ---
interface InlineCode {
_type: 'inlineCode'
code: string
language: string
}
type InlineBlocks = PortableTextSpan | InlineCode
// --- Block styles and list types ---
type TextStyles = 'normal' | 'h1' | 'h2' | 'h3' | 'blockquote' | 'lead'
type ListStyles = 'bullet' | 'number' | 'checkmarks'
// --- Fully narrowed block type ---
type CustomBlock = PortableTextBlock
// --- Custom block-level objects ---
interface ImageBlock {
_type: 'image'
asset: { _ref: string; _type: 'reference' }
alt?: string
}
// --- Final document field type ---
type PortableTextField = CustomBlock | ImageBlock
interface Post {
_id: string
title: string
content: PortableTextField[]
}
```
--------------------------------
### Infer Portable Text Value Type using a Scoped Mock Query
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Define a focused GROQ query for type generation purposes and use its result type with `InferValue`. This strategy keeps the type union tight without manual maintenance, but do not combine it with `SanityQueries[keyof SanityQueries]`.
```tsx
import {type SanityQueries} from '@sanity/client'
import {defineQuery} from 'groq'
const mockQuery = defineQuery(`*[_type in ["author", "category", "post"]]{
_type == "author" => {bio},
_type == "category" => {description},
_type == "post" => {content},
}`)
function CustomPortableText({value}: {value: InferValue}) {
// ...
}
```
--------------------------------
### Custom List and List Item Renderers for React Portable Text
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Maps list types (bullet, number, custom) and list item types to custom React components. Useful for styling different kinds of lists.
```tsx
import { PortableText } from '@portabletext/react'
const components = {
list: {
bullet: ({ children }) => (
),
number: ({ children }) => (
{children}
),
// Custom list type defined in Sanity schema
checkmarks: ({ children }) => (
),
},
listItem: {
bullet: ({ children }) => {children},
number: ({ children }) => {children},
checkmarks: ({ children }) => ✅ {children},
},
}
const blocks = [
{ _type: 'block', _key: 'k1', listItem: 'bullet', level: 1,
children: [{ _type: 'span', _key: 's1', text: 'First item', marks: [] }], markDefs: [] },
{ _type: 'block', _key: 'k2', listItem: 'bullet', level: 1,
children: [{ _type: 'span', _key: 's2', text: 'Second item', marks: [] }], markDefs: [] },
{ _type: 'block', _key: 'k3', listItem: 'checkmarks', level: 1,
children: [{ _type: 'span', _key: 's3', text: 'Done', marks: [] }], markDefs: [] },
]
export function Lists() {
return
}
```
--------------------------------
### Rename 'blocks' prop to 'value'
Source: https://github.com/portabletext/react-portabletext/blob/main/MIGRATING.md
The main input prop for Portable Text content has been renamed from `blocks` to `value`.
```jsx
// From:
// ✅ To:
```
--------------------------------
### Rename 'serializers' prop to 'components'
Source: https://github.com/portabletext/react-portabletext/blob/main/MIGRATING.md
The prop for custom renderers has been renamed from `serializers` to `components` to better reflect its role.
```jsx
// From:
// ✅ To:
```
--------------------------------
### `mergeComponents()` — merge component maps
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Utility that deep-merges a base `PortableTextReactComponents` object with a partial override map. The `block`, `list`, `listItem`, `marks`, and `types` keys are merged as objects; all other keys are replaced. Useful for building layered or theme-based component systems.
```APIDOC
## `mergeComponents()` — merge component maps
### Description
Utility that deep-merges a base `PortableTextReactComponents` object with a partial override map. The `block`, `list`, `listItem`, `marks`, and `types` keys are merged as objects; all other keys are replaced. Useful for building layered or theme-based component systems.
### Usage
**Merge brand components:**
```tsx
import { defaultComponents, mergeComponents } from '@portabletext/react'
const brandComponents = mergeComponents(defaultComponents, {
block: {
h1: ({ children }) => {children}
,
h2: ({ children }) => {children}
,
},
marks: {
strong: ({ children }) => (
{children}
),
},
})
```
**Merge for a specific page section:**
```tsx
import { defaultComponents, mergeComponents } from '@portabletext/react'
const brandComponents = mergeComponents(defaultComponents, {
block: {
h1: ({ children }) => {children}
,
h2: ({ children }) => {children}
,
},
marks: {
strong: ({ children }) => (
{children}
),
},
})
// Later, merge again for a specific page section — h1/h2/strong overrides are preserved
const darkSectionComponents = mergeComponents(brandComponents, {
block: {
normal: ({ children }) => {children}
,
},
})
```
```
--------------------------------
### Custom Block Style Renderers for React Portable Text
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Maps block style values to custom React components. Useful for creating distinct visual styles for headings, quotes, and custom block types.
```tsx
import { PortableText, type PortableTextReactComponents } from '@portabletext/react'
import { toPlainText } from '@portabletext/react'
import slugify from 'slugify'
// Linkable header that generates an id from the block text
const LinkableHeader = ({ children, value }) => {
const slug = slugify(toPlainText(value), { lower: true })
return (
)
}
const components: Partial = {
block: {
normal: ({ children }) => {children}
,
h1: ({ children }) => {children}
,
h2: LinkableHeader,
h3: ({ children }) => {children}
,
blockquote: ({ children }) => (
{children}
),
// Custom block style defined in your Sanity schema
lead: ({ children }) => (
{children}
),
},
}
export function Article({ value }) {
return
}
```
--------------------------------
### Customize List Item Rendering in React Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Customize the rendering of different list item types (e.g., bulleted, checklist) by providing custom React components in the `listItem` object within the `components` prop. This allows for specific styling or content for each list item type.
```jsx
const components = {
listItem: {
// Ex. 1: customizing common list types
bullet: ({children}) => {children},
// Ex. 2: rendering custom list items
checkmarks: ({children}) => ✅ {children},
},
}
```
--------------------------------
### Fallback Components for Unknown Types in React Portable Text
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Defines fallback components for unknown block types, marks, styles, lists, and list items. Useful for debugging or handling unexpected data structures.
```tsx
import { PortableText } from '@portabletext/react'
const components = {
// Render unknown block types as a visible dev note in non-production
unknownType: ({ value, isInline }) => {
if (process.env.NODE_ENV === 'production') return null
const Wrapper = isInline ? 'span' : 'div'
return (
Unknown block type: {value._type}
)
},
// Render unknown marks by passing through children unstyled
unknownMark: ({ children }) => <>{children}>,
}
```
--------------------------------
### components.types - Custom Block and Inline Object Types
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Register custom React components for specific Portable Text `_type` fields, whether they appear as block-level objects or inline objects within text.
```APIDOC
## components.types
### Description
Allows you to define custom React components for rendering specific Portable Text object types, distinguishing between block-level and inline objects.
### Usage
```tsx
import { PortableText } from '@portabletext/react'
import urlBuilder from '@sanity/image-url'
import { getImageDimensions } from '@sanity/asset-utils'
const imageBuilder = urlBuilder({ projectId: 'abc123', dataset: 'production' })
const SanityImage = ({ value, isInline }) => {
const { width, height } = getImageDimensions(value)
return (
)
}
const CallToAction = ({ value, isInline }) =>
isInline ? (
{value.text}
) : (
)
const components = {
types: {
image: SanityImage,
callToAction: CallToAction,
},
}
export function Post({ portableTextValue }) {
return
}
```
### Component Props
- **value**: The Portable Text object data for the current node.
- **isInline** (boolean): Indicates if the object is rendered inline within a text block.
```
--------------------------------
### Infer Portable Text Value Type from All Registered Queries
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Use `InferValue` to create a Portable Text value type that encompasses all possible Portable Text shapes from your registered Sanity queries. This is the preferred method for ensuring comprehensive type coverage.
```tsx
import {type SanityQueries} from '@sanity/client'
type PortableTextValue = InferValue
```
--------------------------------
### Customizing Image and Call-to-Action Types
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Register custom React components for 'image' and 'callToAction' Portable Text types. The `isInline` prop helps differentiate rendering contexts.
```tsx
import { PortableText } from '@portabletext/react'
import urlBuilder from '@sanity/image-url'
import { getImageDimensions } from '@sanity/asset-utils'
const imageBuilder = urlBuilder({ projectId: 'abc123', dataset: 'production' })
const SanityImage = ({ value, isInline }) => {
const { width, height } = getImageDimensions(value)
return (
)
}
const CallToAction = ({ value, isInline }) =>
isInline ? (
{value.text}
) : (
)
const components = {
types: {
image: SanityImage,
callToAction: CallToAction,
},
}
export function Post({ portableTextValue }) {
return
}
```
--------------------------------
### Customize individual block styles
Source: https://github.com/portabletext/react-portabletext/blob/main/MIGRATING.md
Easily override rendering for specific block styles like headings or custom styles using the `components.block` object.
```jsx
// From:
const BlockRenderer = (props) => {
const {style = 'normal'} = props.node
if (/^h\d/.test(style)) {
const level = style.replace(/[^\d]/g, '')
return React.createElement(style, {className: `heading-${level}`}, props.children)
}
if (style === 'blockquote') {
return - {props.children}
}
// Fall back to default handling
return BlockContent.defaultSerializers.types.block(props)
}
;
// ✅ To:
{children}
,
// Same applies to custom styles
customHeading: ({children}) => (
{children}
),
},
}}
/>
```
--------------------------------
### Infer Portable Text Value Type from Specific Named Queries
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
When `SanityQueries[keyof SanityQueries]` results in a large union type, narrow it down by providing a union of specific query result types (e.g., `AuthorQueryResult | PostQueryResult`) to `InferValue`. This requires manual synchronization as queries change.
```tsx
import type {AuthorQueryResult, PostQueryResult} from './sanity.types'
type PortableTextValue = InferValue
```
--------------------------------
### Merge Component Maps with `mergeComponents()`
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Deep-merges a base `PortableTextReactComponents` object with a partial override map. Useful for building layered or theme-based component systems.
```tsx
import { defaultComponents, mergeComponents } from '@portabletext/react'
const brandComponents = mergeComponents(defaultComponents, {
block: {
h1: ({ children }) => {children}
,
h2: ({ children }) => {children}
,
},
marks: {
strong: ({ children }) => (
{children}
),
},
})
```
```tsx
import { defaultComponents, mergeComponents } from '@portabletext/react'
const brandComponents = mergeComponents(defaultComponents, {
block: {
h1: ({ children }) => {children}
,
h2: ({ children }) => {children}
,
},
marks: {
strong: ({ children }) => (
{children}
),
},
})
// Later, merge again for a specific page section — h1/h2/strong overrides are preserved
const darkSectionComponents = mergeComponents(brandComponents, {
block: {
normal: ({ children }) => {children}
,
},
})
```
--------------------------------
### Disable Missing Component Warnings in React Portable Text
Source: https://github.com/portabletext/react-portabletext/blob/main/README.md
Control console warnings when Portable Text encounters an unknown component type. Set `onMissingComponent` to `false` to disable warnings entirely, or provide a custom function to handle these errors, such as reporting them to an error logging service.
```tsx
import {PortableText}
```
```tsx
import {PortableText}
{
myErrorLogger.report(message, {
// eg `someUnknownType`
type: options.type,
// 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle'
nodeType: options.nodeType
})
}}
/>
```
--------------------------------
### Customizing Hard Break Rendering in React Portable Text
Source: https://context7.com/portabletext/react-portabletext/llms.txt
Controls how newline characters (`\n`) within text spans are rendered. Defaults to `
`. Set to `false` to render the literal newline character.
```tsx
import { PortableText } from '@portabletext/react'
// Default — renders
// Disable hard break rendering (render raw \n)
// Custom hard break component
,
}}
/>
```