What Portable Text stores

Portable Text is an open specification for block content represented as JSON. Text blocks contain spans, with marks for formatting and annotations for information such as links. Blocks and spans have stable keys. A content model can also declare custom objects alongside text blocks.

A short paragraph might look like the example below. The value is data rather than pre-rendered markup. A frontend renderer turns its normal block style into the appropriate HTML element and escapes the text as part of normal rendering.

json
[
  {
    "_type": "block",
    "_key": "intro",
    "style": "normal",
    "markDefs": [],
    "children": [
      { "_type": "span", "_key": "intro-text", "text": "A place for your next idea.", "marks": [] }
    ]
  }
]

Custom blocks belong to your content model

Consider an article with a product callout. Instead of storing a styled HTML card in the body, define a callout block with a product reference and supporting text. A website component can resolve the reference and render the card. A mobile renderer can present the same information differently.

Define the allowed custom block types in the schema. The renderer needs an explicit component for each supported type and a sensible response when it receives an unknown type. An editor should not be able to insert a block that the published website cannot interpret.

Annotations deserve the same attention. A link annotation needs a validated destination and a renderer that permits only appropriate URL schemes. Structured JSON does not make arbitrary embedded HTML or unsafe links harmless.

HTML still has a place

An HTML string is convenient when the main task is displaying already-rendered content on a website. It can be a reasonable output format for feeds, emails, or migration exports. The tradeoff is that content structure and presentation decisions may be mixed into the same string.

Portable Text makes custom content easier to inspect and map to components, but it adds a rendering step. It also requires a conversion policy when importing HTML. A table, an inline widget, and a custom embed do not become supported merely because a converter can produce some JSON. Keep a report of unsupported content and review it before publishing.

Test the editor’s round trip

An editor can look correct while losing information when a document is saved. Create fixtures with overlapping marks, linked text, nested lists, custom blocks, and image references. Load each fixture, edit an unrelated field, save it, and compare the resulting Portable Text structure.

For a collaborative editor, also test two people editing text near an annotation and moving blocks at the same time. Stable block keys help identify content, but the collaboration adapter still needs to preserve the format. An internal editor representation is acceptable only if its supported Portable Text contract survives conversion.

  • Keep block and span keys stable through unrelated edits.
  • Preserve annotation definitions and the marks that reference them.
  • Validate custom blocks against their declared schemas.
  • Check rendering as well as serialized data after concurrent edits.

Blockquote’s format choice

Blockquote has selected Portable Text as its planned rich-text storage and delivery format. Editor fidelity and collaborative persistence are implementation checkpoints. We will document the supported blocks and annotations when the content editor is available; that integration has not shipped yet.

Put it into practice

A standard content format is useful when the schema, editor, converter, and frontend agree on what they support. Test that agreement with real documents.

Sources and further reading

These guides explain our design choices. For shipped functionality and current availability, see the product status.