Rendering a post
What content.html contains, and how to use the table of contents and SEO fields.
The body
content.html is the post body, rendered by Horbit from the visual editor and sanitised:
no scripts, no inline event handlers and no inline styles beyond --hb-* CSS variables.
Embeds from YouTube, Vimeo, Loom and Spotify are the only iframes.
It is wrapped in one element, so it drops straight into your layout:
<div class="hb-content" data-hb-version="1">
<section class="hb-row hb-row--cols-1 hb-row--width-content">
<div class="hb-row__inner">
<div class="hb-col hb-col--span-12">
<div class="hb-block hb-text" data-block-type="text" data-block-id="blk-1">
<p class="hb-paragraph">Open data changes who gets to ask questions.</p>
</div>
<h2 id="licences-matter" class="hb-block hb-heading hb-heading--h2">Licences matter</h2>
</div>
</div>
</section>
</div>Table of contents
content.toc lists the post's headings in order. Each id matches the heading's id attribute, so #licences-matter links straight to it.
post.content.toc
// [{ id: 'licences-matter', text: 'Licences matter', level: 2 }, …]Around the body
Everything else you need for a post page comes with it:
| Field | Use |
|---|---|
title, excerpt | Headline and standfirst. excerpt falls back to the opening lines. |
featuredImage | { url, alt, caption } or null. |
author | { id, name, avatarUrl }. |
categories, tags | For chips and links. Categories carry an optional color. |
publishedAt, updatedAt, readingTimeMinutes | Bylines. |
seo | title, description, canonicalUrl, ogImageUrl — ready for <head>. |
stats | Views, comments, reactions and counts per reaction type. |
commentsOpen, reactionsOpen | Whether to show the comment form and reaction bar. |
export async function generateMetadata({ params }) {
const post = await getPost((await params).slug);
return {
title: post.seo.title,
description: post.seo.description,
alternates: post.seo.canonicalUrl ? { canonical: post.seo.canonicalUrl } : undefined,
openGraph: { images: post.seo.ogImageUrl ? [post.seo.ogImageUrl] : [] },
};
}Rendering blocks yourself
Ask for ?include=design to get the editor's structure — rows of columns of typed blocks — and render each block with your own components.
data-block-type and data-block-id on every rendered block make it easy to mix the two: render the HTML, then hydrate particular blocks.
Syntax highlighting
Code blocks come out as <code class="hb-code__code language-ts">, which Prism, highlight.js and Shiki all recognise.