---
title: Serialize Portable Text to React
description: Render Portable Text in React and Next.js using @portabletext/react
tags: [portable-text, react, nextjs, serialization, rendering]
---
# Serialize Portable Text to React
Use `@portabletext/react` (or re-exported from `next-sanity`) to render PT in React/Next.js.
```bash
npm install @portabletext/react
```
## Basic Usage
```tsx
import {PortableText} from '@portabletext/react'
// or: import {PortableText} from 'next-sanity'
export function Body({value}: {value: PortableTextBlock[]}) {
return
}
```
## Typed Components Object
```tsx
import type {PortableTextComponents} from '@portabletext/react'
const components: PortableTextComponents = {
// Block styles
block: {
h1: ({children}) =>
{children}
,
h2: ({children}) => {children}
,
blockquote: ({children}) => (
{children}
),
// 'normal' is the default paragraph style
},
// Custom block types
types: {
image: ({value}) => (
),
code: ({value}) => (
{value.code}
),
},
// Marks (decorators + annotations)
marks: {
// Decorator
highlight: ({children}) => (
{children}
),
// Annotation
link: ({children, value}) => {
const rel = !value?.href?.startsWith('/') ? 'noreferrer noopener' : undefined
return (
{children}
)
},
internalLink: ({children, value}) => (
{children}
),
},
// Lists
list: {
bullet: ({children}) => ,
number: ({children}) => {children}
,
},
listItem: {
bullet: ({children}) => {children},
},
}
```
## Props Reference
| Component type | Props received |
|---------------|----------------|
| `block.*` | `{children, value}` — `value` is the full block |
| `types.*` | `{value, isInline}` — `value` is the custom block data |
| `marks.*` | `{children, value, markType, markKey}` — `value` is the markDef data |
| `list.*` | `{children, value}` |
| `listItem.*` | `{children, value}` |
## Performance: Stabilize the Components Object
❌ **Bad** — recreated every render:
```tsx
function Body({value}) {
return
}
}} />
}
```
✅ **Good** — defined outside or memoized:
```tsx
const components: PortableTextComponents = {
types: {image: ({value}) =>
}
}
function Body({value}) {
return
}
```
## Plain Text Extraction
```tsx
import {toPlainText} from '@portabletext/react'
const text = toPlainText(blocks) // for meta descriptions, search indexing
```
## Tailwind Typography Shortcut
For simple blogs without custom blocks, wrap in `prose`:
```tsx
```
## Reference
- [@portabletext/react](https://github.com/portabletext/react-portabletext)
- [Sanity docs: Presenting Portable Text](https://www.sanity.io/docs/presenting-block-text)