Ricos is Wix's rich-content format — a tree of typed nodes serialized as JSON. The same structure is embedded by many products: a Blog post's draftPost.richContent, a Store product's rich description, an Events description, and CMS rich-text fields all expect a Ricos document. This recipe is the authoring reference for that node tree: the valid shape of each node, how nodes nest, and how to format text. It is intentionally product-agnostic — the consuming API decides where the document goes; this recipe governs what a valid document looks like.
A Ricos document is an object with a nodes array: { "nodes": [ /* block nodes */ ] }. Whatever field the consuming API exposes (e.g. richContent), it holds this object. For validating or converting an existing document to/from HTML/Markdown, see Ricos Converter Service.
type is always a bare string — "type": "PARAGRAPH", never an object like "type": { "type": "PARAGRAPH" }. An object-valued type may pass a shallow validation but renders as a broken/uneditable block.type, an optional id, and (for container nodes) a nodes array of children. Node ids are optional when authoring for a create request — the API generates them; the examples below omit id for brevity.PARAGRAPH, HEADING, or CODE_BLOCK. It must never sit directly in the root nodes array or inside a LIST_ITEM, BLOCKQUOTE, or TABLE_CELL — those must contain a PARAGRAPH (or HEADING) that then contains the TEXT. See Nesting rules.PARAGRAPH — the base text container. An empty paragraph — { "type": "PARAGRAPH" } — acts as a vertical spacer. paragraphData.textStyle.textAlignment accepts AUTO·LEFT·CENTER·RIGHT·JUSTIFY:
HEADING — same TEXT-in-container shape as PARAGRAPH, with the level (1–6) in headingData:
BULLETED_LIST / ORDERED_LIST — nesting is LIST → LIST_ITEM → PARAGRAPH → TEXT. Ordered lists use orderedListData in place of bulletedListData:
BLOCKQUOTE — wraps a PARAGRAPH (never a bare TEXT):
DIVIDER — a standalone horizontal rule (no children). lineStyle: SINGLE·DOUBLE·DASHED·DOTTED; width: LARGE·MEDIUM·SMALL:
TABLE — nesting is TABLE → TABLE_ROW → TABLE_CELL → PARAGRAPH → TEXT. tableData.dimensions.colsWidthRatio sets relative column widths. Fill a header row or zebra-stripe body rows with tableCellData.cellStyle.backgroundColor (a hex string):
CODE_BLOCK — children are TEXT nodes (one per line, or \n-joined):
IMAGE — references a Wix Media id (upload/import the image first via Media Manager; a raw external URL will not render). Requires width and height. An optional CAPTION child holds a TEXT node:
Apply formatting with the decorations array on a TEXT node. Each decoration is an object with a type and (for some types) a data field:
| Decoration | Data field |
|---|---|
BOLD | fontWeightValue: 700 |
ITALIC | italicData: true |
UNDERLINE | (none) |
STRIKETHROUGH | strikethroughData: true |
COLOR | colorData: { foreground: "#hex" } (add background for highlight) |
LINK | linkData: { link: { url, target: "BLANK" } } |
FONT_SIZE | fontSizeData: { unit: "PX", value: 24 } |
foreground for colors.\n inside textData.text — one visual line is one node. Emit separate sibling PARAGRAPH/HEADING nodes for separate lines.Assemble the shapes above into one valid richContent document. This example exercises every common node type — heading, bulleted list, ordered list, blockquote, filled-header table, divider, code block, and a paragraph with mixed bold + link runs — all correctly nested. Copy its structure; replace the placeholder text with real content.
Note the mixed-run paragraph at the end: the linked words are their own TEXT node carrying BOLD + LINK, while the surrounding words are separate plain TEXT runs — that is how you apply formatting to part of a sentence.
| Parent | Valid children |
|---|---|
Root nodes | PARAGRAPH, HEADING, BULLETED_LIST, ORDERED_LIST, BLOCKQUOTE, DIVIDER, IMAGE, TABLE, CODE_BLOCK |
| PARAGRAPH / HEADING / CODE_BLOCK | TEXT |
| BULLETED_LIST / ORDERED_LIST | LIST_ITEM |
| LIST_ITEM / BLOCKQUOTE | PARAGRAPH (which then contains TEXT) |
| TABLE → TABLE_ROW → TABLE_CELL | cell contains PARAGRAPH / HEADING / IMAGE |
| IMAGE | CAPTION (optional) |
All decidable from the JSON itself — check before handing the document to a consuming API:
type is a bare string — search for "type": {; there should be zero hits.LIST_ITEM, a BLOCKQUOTE, or a TABLE_CELL.LIST → LIST_ITEM → PARAGRAPH → TEXT and TABLE → TABLE_ROW → TABLE_CELL → PARAGRAPH → TEXT, no level skipped.level (1–6) and nest logically (don't jump H2 → H4).\n inside textData.text — split into sibling nodes; mixed inline formatting → split into multiple TEXT runs.id (not a raw URL), with width, height, and meaningful altText.LINK decoration has a valid url and target.Last updated: 2 July 2026