npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@portabletext/astro

v0.1.0

Published

Render Portable Text with Astro

Readme

@portabletext/astro

npm version npm downloads license

Render Portable Text with Astro.

[!NOTE] This package is a fork of astro-portabletext by Tom Theisel, maintained under the @portabletext organization. All credit for the original design and implementation goes to Tom. It remains distributed under the original ISC License. See Migrating from astro-portabletext.

Table of contents

Installation

npm install @portabletext/astro

Prerequisites: Astro v4.6 or newer.

Basic usage

Import the PortableText component and pass it a Portable Text value. The library provides sensible defaults for rendering all standard Portable Text elements, which you can override.

---
import {PortableText} from '@portabletext/astro'

const portableText = [
  {
    _type: 'block',
    style: 'normal',
    markDefs: [],
    children: [
      {_type: 'span', marks: [], text: 'This is a '},
      {_type: 'span', marks: ['strong'], text: 'bold'},
      {_type: 'span', marks: [], text: ' text example!'},
    ],
  },
]
---

<PortableText value={portableText} />

Sanity integration

This library's predecessor is officially recommended by Sanity for rendering Portable Text in Astro projects. Helpful resources:

Customizing components

Default components

Default components are provided for all standard features of the Portable Text spec, with logical HTML defaults. Provided components are merged with the defaults, so you only need to provide the things you want to override.

{
  type: {
    /* Custom types go here */
  },
  block: {
    h1: /* <h1 {...attrs}><slot /></h1> */,
    h2: /* <h2 {...attrs}><slot /></h2> */,
    h3: /* <h3 {...attrs}><slot /></h3> */,
    h4: /* <h4 {...attrs}><slot /></h4> */,
    h5: /* <h5 {...attrs}><slot /></h5> */,
    h6: /* <h6 {...attrs}><slot /></h6> */,
    blockquote: /* <blockquote {...attrs}><slot /></blockquote> */,
    normal: /* <p {...attrs}><slot /></p> */
  },
  list: {
    bullet: /* <ul {...attrs}><slot /></ul> */,
    number: /* <ol {...attrs}><slot /></ol> */,
    menu: /* <menu {...attrs}><slot /></menu> */,
  },
  listItem: {
    bullet: /* <li {...attrs}><slot /></li> */,
    number: /* <li {...attrs}><slot /></li> */,
    menu: /* <li {...attrs}><slot /></li> */,
  },
  mark: {
    code: /* <code {...attrs}><slot /></code> */,
    em: /* <em {...attrs}><slot /></em> */,
    link: /* <a {...attrs} href="..."><slot /></a> */,
    'strike-through': /* <del {...attrs}><slot /></del> */,
    strong: /* <strong {...attrs}><slot /></strong> */,
    underline: /* <span {...attrs} style="text-decoration: underline;"><slot /></span> */
  },
  text: /* Renders plain text */,
  hardBreak: /* <br /> */,
}

Custom components

Custom components give you control over how each node is rendered. Map a component to a whole node type, or to a specific property (style, mark type, list item type, etc.) of that node type.

---
import {PortableText} from '@portabletext/astro'
import Code from '../components/Code.astro'
import Link from '../components/Link.astro'

const portableText = [
  // ... your Portable Text content
]

const components = {
  // Custom object types, keyed by `_type` (or a single component for all types)
  type: {code: Code},
  // Block styles, keyed by `style`
  block: {/* h1, h2, normal, ... */},
  // Lists, keyed by `listItem`
  list: {/* bullet, number, ... */},
  // List items, keyed by `listItem`
  listItem: {/* bullet, number, ... */},
  // Marks (decorators and annotations), keyed by mark type
  mark: {link: Link},
  // Fallbacks for unknown nodes
  unknownType: undefined,
  unknownBlock: undefined,
  unknownList: undefined,
  unknownListItem: undefined,
  unknownMark: undefined,
  // Plain text spans and hard breaks
  text: undefined,
  hardBreak: undefined,
}
---

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

Each custom component receives node, index and isInline props, and renders any children through a <slot />. For example, a custom link mark:

---
import type {MarkProps} from '@portabletext/astro/types'

export type Props = MarkProps<{href?: string}>

const {node} = Astro.props
const href = node.markDef?.href
---

<a href={href}><slot /></a>

Slots

Slots provide a flexible way to enhance rendering by passing additional props to the resolved component - for example applying custom classes or wrapping elements - without replacing the default component entirely.

---
import {PortableText} from '@portabletext/astro'

const portableText = [
  // ... your Portable Text content
]
---

<PortableText value={portableText}>
  <fragment slot="mark">
    {({Component, props, children}) => (
      <Component {...props} class="mark">{children}</Component>
    )}
  </fragment>
</PortableText>

<style>
  .mark:where(strong) {
    /* some styles */
  }
</style>

PortableText component properties

| Property | Type | Description | | ------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | value | array or object | Portable Text payload | | components (optional) | object | Mapping of components to node types or their properties. | | onMissingComponent (optional) | function or boolean | Disable warning messages or handle unknown types. Default prints to console. | | listNestingMode (optional) | "html" or "direct" | List nesting mode. Default is html. See ToolkitListNestMode. |

Utility functions

import {usePortableText, mergeComponents, toPlainText, spanToPlainText} from '@portabletext/astro'

usePortableText

Within a component passed into the components prop, usePortableText(node) returns rendering utilities scoped to that node: getDefaultComponent(), getUnknownComponent() and render().

---
import type {BlockProps} from '@portabletext/astro/types'
import {usePortableText} from '@portabletext/astro'

export type Props = BlockProps

const {node} = Astro.props
const {getDefaultComponent} = usePortableText(node)
const Default = getDefaultComponent()
---

<Default {...Astro.props}><slot /></Default>

mergeComponents

Merges two component maps, giving priority to the overrides. Useful for extending a shared base set of components.

import {mergeComponents} from '@portabletext/astro'

const components = mergeComponents(baseComponents, {
  block: {h1: MyHeading},
})

toPlainText

Renders one or more Portable Text blocks as a plain string - handy for meta descriptions or generating slugs. spanToPlainText does the same for a single span's children.

---
import {toPlainText} from '@portabletext/astro'

const {node} = Astro.props
const text = toPlainText(node)
---

Migrating from astro-portabletext

@portabletext/astro is a drop-in fork of astro-portabletext. To migrate, swap the dependency and update your imports:

- import {PortableText} from 'astro-portabletext'
+ import {PortableText} from '@portabletext/astro'

- import type {BlockProps} from 'astro-portabletext/types'
+ import type {BlockProps} from '@portabletext/astro/types'

The component API, props and utility functions are unchanged.

License

ISC © Tom Theisel (original author) and the Portable Text authors.