Horbit Developers

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:

FieldUse
title, excerptHeadline and standfirst. excerpt falls back to the opening lines.
featuredImage{ url, alt, caption } or null.
author{ id, name, avatarUrl }.
categories, tagsFor chips and links. Categories carry an optional color.
publishedAt, updatedAt, readingTimeMinutesBylines.
seotitle, description, canonicalUrl, ogImageUrl — ready for <head>.
statsViews, comments, reactions and counts per reaction type.
commentsOpen, reactionsOpenWhether to show the comment form and reaction bar.
Next.js metadata
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.

On this page