4.9 KiB
4.9 KiB
title, description, tags
| title | description | tags | |||||
|---|---|---|---|---|---|---|---|
| Manually Construct Portable Text Blocks | Build Portable Text blocks programmatically from any data source |
|
Manually Construct Portable Text Blocks
Build PT blocks directly when converting from non-HTML sources (APIs, databases, custom formats) or when you need precise control over the output.
Key Generation
Every block, span, and markDef needs a unique _key:
import {randomKey} from '@sanity/util/content'
const key = randomKey(12) // e.g., "a1b2c3d4e5f6"
Or use a simple helper:
const randomKey = () => Math.random().toString(36).slice(2, 14)
Building Blocks
Simple Paragraph
{
_type: 'block',
_key: randomKey(),
style: 'normal',
children: [
{_type: 'span', _key: randomKey(), text: 'Hello world', marks: []}
],
markDefs: []
}
Heading
{
_type: 'block',
_key: randomKey(),
style: 'h2', // h1, h2, h3, h4, h5, h6
children: [
{_type: 'span', _key: randomKey(), text: 'Section Title', marks: []}
],
markDefs: []
}
Text with Decorators (Bold, Italic, Code)
{
_type: 'block',
_key: randomKey(),
style: 'normal',
children: [
{_type: 'span', _key: randomKey(), text: 'This is ', marks: []},
{_type: 'span', _key: randomKey(), text: 'bold', marks: ['strong']},
{_type: 'span', _key: randomKey(), text: ' and ', marks: []},
{_type: 'span', _key: randomKey(), text: 'italic', marks: ['em']},
{_type: 'span', _key: randomKey(), text: ' text.', marks: []},
],
markDefs: []
}
Text with Annotations (Links)
Annotations require a markDef entry and a matching key in marks:
const linkKey = randomKey()
{
_type: 'block',
_key: randomKey(),
style: 'normal',
children: [
{_type: 'span', _key: randomKey(), text: 'Visit ', marks: []},
{_type: 'span', _key: randomKey(), text: 'Sanity', marks: [linkKey]},
{_type: 'span', _key: randomKey(), text: ' for more.', marks: []},
],
markDefs: [
{_type: 'link', _key: linkKey, href: 'https://www.sanity.io'}
]
}
Overlapping Marks
A span can have multiple marks (both decorators and annotations):
const linkKey = randomKey()
// "bold link" — both strong and linked
{_type: 'span', _key: randomKey(), text: 'bold link', marks: ['strong', linkKey]}
Lists
Lists are regular blocks with listItem and level:
// Bullet list
[
{
_type: 'block', _key: randomKey(), style: 'normal',
listItem: 'bullet', level: 1,
children: [{_type: 'span', _key: randomKey(), text: 'First item', marks: []}],
markDefs: []
},
{
_type: 'block', _key: randomKey(), style: 'normal',
listItem: 'bullet', level: 1,
children: [{_type: 'span', _key: randomKey(), text: 'Second item', marks: []}],
markDefs: []
},
{
_type: 'block', _key: randomKey(), style: 'normal',
listItem: 'bullet', level: 2, // nested
children: [{_type: 'span', _key: randomKey(), text: 'Nested item', marks: []}],
markDefs: []
},
]
Custom Block Types
Any object with _type and _key can be a block:
// Image block
{
_type: 'image',
_key: randomKey(),
asset: {_type: 'reference', _ref: 'image-abc123-800x600-png'},
alt: 'A description',
}
// Code block
{
_type: 'code',
_key: randomKey(),
language: 'typescript',
code: 'const x = 42',
}
// YouTube embed
{
_type: 'youtube',
_key: randomKey(),
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
}
Helper Function
A utility for building common blocks:
function createBlock(
text: string,
style: string = 'normal',
options?: {listItem?: string; level?: number}
) {
return {
_type: 'block',
_key: randomKey(),
style,
...(options?.listItem ? {listItem: options.listItem, level: options.level || 1} : {}),
children: [{_type: 'span', _key: randomKey(), text, marks: []}],
markDefs: [],
}
}
// Usage
const blocks = [
createBlock('Introduction', 'h2'),
createBlock('This is a paragraph.'),
createBlock('First point', 'normal', {listItem: 'bullet', level: 1}),
createBlock('Second point', 'normal', {listItem: 'bullet', level: 1}),
]
Validation Checklist
Before writing PT blocks to Sanity, verify:
- Every block has
_typeand_key - Every span has
_type: "span",_key,text, andmarks - Every annotation key in
marks[]has a matching entry inmarkDefs[] markDefsentries have_typeand_key_keyvalues are unique within the arraystylevalues match your schema's allowed styleslistItemvalues match your schema's allowed list types- Custom block
_typevalues match registered schema types
Reference
- Portable Text Specification
- @sanity/util — provides
randomKey()for generating_keyvalues