--- 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}) => ( {value.alt ), 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)