Skip to main content
?format=blocks serves the article body as a flat, ordered array of top-level blocks instead of one HTML string. Everything you need to render it is on this page.

When to choose blocks over HTML

Choose blocks when you want to render your own components for images and video. With ?format=html an image is our <img> (inside a <figure> when the author captioned it) and a video is a <div> carrying the address, not a player; with blocks you get the image’s src, alt, caption and decorative and the video’s src and provider as plain fields, and can pass them to your site’s responsive image component, your CDN transform, or your own video player. If your articles carry video, you need blocks, or you need to handle that <div> yourself. We do not put a third-party <iframe> in the HTML we serve you, so a video embed renders as nothing on the page until your site turns the element into a player. Articles describes the element and what is on it. Choose HTML for everything else. Blocks are not a richer text model and give you nothing extra for paragraphs and headings. You do not have to choose per site. The two formats are two views of the same stored render, so you can switch by changing one query parameter, and an article requested in both formats describes the same content: same text, same image addresses and captions, same video addresses. What differs is what a browser does with it, which is the video point above.

The envelope

blocks is flat and in document order. Blocks never nest: there is no block that contains other blocks, so rendering is one pass over the array. contract_version is the version of the block shape this article was rendered against. It is what a future change to the block shape would key off. Today it is 1. Read it if you want to be defensive; you do not need to branch on it.

The block types

type is one of exactly these eight names. They are ours, and they are the whole set.

Text-bearing blocks

paragraph, heading, blockquote, bullet_list and ordered_list each carry a single field: The fragment is a complete element, tag included: a paragraph is "<p>…</p>", not the text inside one. Inline formatting (bold, italic, links) is already in the fragment, and so is the heading’s level, so a heading block tells you whether it is an <h2> or an <h3> by what it contains. These fragments are the same bytes ?format=html is joined from, in the same order. That is why the two formats cannot describe different content: the text is literally the same HTML, cut at the top-level boundaries. The fragments are sanitised when the article is published, which is what makes them safe to inject as HTML. There is no inline-level structure to walk: if you need one, the html string is what you have. html is the only pre-sanitised field on this surface. Every other field, the media blocks’ src, alt and caption and the article’s own title, excerpt and feature_image_url, is plain text an author typed and is not escaped for you. Set those through the DOM (textContent, img.alt = …) instead of building markup out of them: an author writing Kane's "winner" in an alt box breaks a quoted attribute with no malice at all.

image

A decorative image is one the author declared purely visual: a divider, a flourish, a repeat of the caption. alt is an empty string on one, which is what tells a screen reader to skip it, so binding alt straight onto your <img> already does the right thing and you can ignore the flag. Read it if you do something else with the field, like falling back to the caption when alt is blank: that fallback is wrong on a decorative image. Handle src: null by rendering nothing for that block. It means the address did not pass our URL rules and was blanked at publish, so there is no image to fetch.

video_embed

src is stored exactly as the author pasted it and is not normalised, so it can be any address the editor accepted for that provider: a watch page, a share link, a /shorts/ or /live/ page, a player or embed URL. The block carries no video id, so if you want to build your own player you have to read the id out of src. These are the shapes the editor accepts, and the whole set of them: A YouTube id is eleven characters of A-Z a-z 0-9 _ -; a Vimeo id is digits. Reading it back out covers every row above:
Treat provider as an open set even though two values ship today, and fall back to a plain link to src for a value you do not recognise, or when videoId comes back null. Render nothing for a src of null.

divider

Carries no fields at all:

Rendering it

A minimal renderer is a switch with eight arms and a default:
Keep that default arm. The eight types above are the whole set today, and a type you do not recognise can only mean a block type added after your code was written. Skipping it costs a reader one block; throwing costs them the article. The two media arms hand src, alt and caption to your own components, which is where the escaping rule above applies: build those nodes rather than concatenating the fields into markup. Render a club’s articles has the same renderer written that way. A short renderer that only wants its own media components is shorter still: intercept image and video_embed, and pass everything else through as block.html ?? "".

Where to go next