Using annotations or blocks for dynamic content in Sanity.io
Based on the documentation and the specific use case you're describing, annotations are the right choice for WordPress shortcode-like functionality that needs to be inline with text.
Here's why annotations are the better fit:
Annotations are designed for inline content. They're specifically built to be embedded within text spans in Portable Text, making them perfect for inline dynamic content like shortcodes. When you add an annotation, it appears inline with the paragraph text by default - exactly what you need.
Blocks are not inline. As you correctly noted, blocks in Portable Text are standalone content units that sit between paragraphs, not within them. While you can technically create "inline blocks" (objects with inlineBlock: true in your schema), they're more complex and less commonly used. The term "block" itself indicates they're meant to be distinct content units.
How to implement shortcode-like functionality with annotations:
- Define custom annotation types in your schema for each shortcode type you need
- Store any dynamic values either in the annotation itself or in hidden/read-only fields on the document
- Render them on the frontend with custom serializers that replace the annotation with the actual dynamic content
There's a helpful community thread where someone solved almost exactly your use case. They initially tried inline blocks with references but found that annotations combined with custom input components worked better - the annotation marks the position in text, and a custom input component lets editors select which variable to insert.
Quick example schema:
marks: {
annotations: [
{
name: 'dynamicVariable',
type: 'object',
title: 'Dynamic Variable',
icon: CodeIcon,
fields: [
{
name: 'variableType',
type: 'string',
title: 'Variable Type',
options: {
list: [
{title: 'User First Name', value: 'userFirstName'},
{title: 'Current Date', value: 'currentDate'},
{title: 'Product Price', value: 'productPrice'}
]
}
}
]
}
]
}Then in your frontend, you'd create a custom serializer for the dynamicVariable annotation that renders the actual dynamic value:
// Example with @portabletext/react
const components = {
marks: {
dynamicVariable: ({value, children}) => {
// Replace with actual dynamic content
const dynamicValue = getDynamicValue(value.variableType);
return <span className="dynamic-var">{dynamicValue}</span>;
}
}
}The inline nature, the ability to carry metadata, and the straightforward rendering process make annotations the clear choice for WordPress shortcode-style functionality in Sanity. Your instinct about annotations being inline by default is spot on - that's exactly what makes them perfect for this use case.
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.