Skip to content

Portable Text

Portable Text renders rich text content with embedded blocks. It follows the Portable Text πŸ”— specification and allows for rich formatting and embedded content.

Schema Configuration

Sanity Schema

// In your feature descriptor
import { PortableTextDescriptor } from '@vyuh/sanity-schema-system';
export const myFeature = new FeatureDescriptor({
name: 'myFeature',
title: 'My Feature',
contents: [
new PortableTextDescriptor({
// Optional: Define custom marks and blocks
marks: [
// Custom marks
],
blocks: [
// Custom blocks that can be embedded
],
}),
],
});

Content Structure

// Portable Text content object structure
interface PortableText {
_type: 'vyuh.portableText';
blocks: {
_type: 'block';
style: string;
children: {
_type: 'span';
text: string;
marks?: string[];
}[];
markDefs?: {
_key: string;
_type: string;
// Mark-specific properties
}[];
}[];
}

Extensibility

Schema Descriptors

The PortableTextDescriptor in the schema system provides the following extensibility points:

  • annotations: Register custom marks (annotations) for text formatting
  • blocks: Define custom block types that can be embedded within portable text
  • styles: Define custom block styles beyond the default ones
// Schema-side descriptor
new PortableTextDescriptor({
annotations: [highlightMark, tooltipMark],
blocks: [codeBlock, { type: 'vyuh.card' }],
styles: [{ title: 'Callout', value: 'callout' }]
})

React Descriptors

In the React system, portable text content is configured using the PortableTextDescriptor:

// React-side registration
import { PortableTextDescriptor } from '@vyuh/react-feature-system';
import { ContentExtensionDescriptor } from '@vyuh/react-extension-content';
import { FeatureDescriptor } from '@vyuh/react-core';
new FeatureDescriptor({
name: 'myFeature',
extensions: [
new ContentExtensionDescriptor({
contents: [
new PortableTextDescriptor({
blockTypes: [
{ type: 'codeBlock', component: CodeBlockComponent },
{ type: 'vyuh.card', component: PortableTextConfig.shared.renderContentItem },
],
marks: [
{ type: 'highlight', component: HighlightComponent },
],
blockStyles: [
{ style: 'callout', component: CalloutComponent },
],
}),
],
}),
],
});