Block Development

Create custom blocks for HugoBlox. Learn how to build Go HTML or Preact blocks with co-located CSS, Tailwind support, and the block engine internals.

The HBX Resolver is the engine powering HugoBlox's modular architecture. It dynamically resolves block definitions (Blox) from your content front matter into rendered HTML components.

Core Concepts

Dynamic Loading

Blocks are loaded on-demand based on your content configuration.

Schema Validation

Parameters are validated against JSON schemas for type safety.

Extensible

Create custom blocks without modifying the core engine.

Block Types

HugoBlox supports two types of blocks:

TypeFilesBest For
Go HTMLblock.htmlServer-rendered content, simple layouts
Preactcomponent.jsx + client.jsxInteractive UI, animations, complex state

Both types support co-located style.css for custom styling with full Tailwind @apply support.

Block Structure

block.html
style.css (optional)
manifest.json (optional)
component.jsx
client.jsx
style.css (optional)
manifest.json (optional)

How it Works

  1. Definition: You define a list of sections in your page front matter.
  2. Resolution: Hugo iterates through each section and looks for the block parameter.
  3. Lookup: The HBX Resolver searches for a matching template in layouts/_partials/hbx/blocks/<block-name>/.
  4. Rendering: For Go HTML blocks, block.html is rendered server-side. For Preact blocks, component.jsx is hydrated client-side via client.jsx.
  5. CSS: Any co-located style.css is auto-discovered and processed through the Tailwind pipeline.

Creating a Go HTML Block

1. Create the Template

Create layouts/_partials/hbx/blocks/my-block/block.html:

{{ $block := . }}
{{ $title := $block.content.title | default "Default Title" }}

<section class="my-block p-8">
  <h2 class="text-2xl font-bold">{{ $title }}</h2>
  <div class="content">
    {{ $block.content.text | markdownify }}
  </div>
</section>

2. Use it in Content

# content/_index.md
type: landing
sections:
  - block: my-block
    content:
      title: My New Block
      text: It works!

Creating a Preact Block

1. Create the Component

Create layouts/_partials/hbx/blocks/my-block/component.jsx:

export const MyBlock = ({ content, design }) => {
  return (
    <section class="py-24">
      <h1 class="text-4xl font-bold">{content.title}</h1>
      <p class="mt-4 text-lg text-gray-600">{content.text}</p>
    </section>
  );
};

2. Create the Client Entry

Create layouts/_partials/hbx/blocks/my-block/client.jsx:

import { render } from "preact";
import { MyBlock } from "./component.jsx";

function renderMyBlocks() {
  const blocks = document.querySelectorAll('[data-block-type="my-block"]');
  blocks.forEach((block) => {
    const props = JSON.parse(block.dataset.props);
    render(<MyBlock {...props} />, block);
  });
}

renderMyBlocks();

3. Use it in Content

sections:
  - block: my-block
    content:
      title: Interactive Block
      text: Powered by Preact!

Co-located CSS

Blocks can include a style.css file alongside their template or component. These files are auto-discovered and processed through the Tailwind CSS pipeline, so you get full access to Tailwind directives.

Supported Features

  • @apply — use Tailwind utility classes in your CSS
  • @theme — reference Tailwind theme values
  • CSS nesting — use native CSS nesting syntax
  • CSS variables — reference HugoBlox theme variables like var(--color-primary-500)
  • Dark mode — use .dark selectors

Example

Create layouts/_partials/hbx/blocks/my-block/style.css:

.my-block {
  @apply relative overflow-hidden;

  .my-block-title {
    @apply text-2xl font-bold;
    color: var(--color-primary-700);
  }

  .my-block-card {
    @apply rounded-lg p-6 shadow-md;
    transition: transform 0.2s ease;

    &:hover {
      transform: translateY(-2px);
    }
  }
}

.dark .my-block .my-block-title {
  color: var(--color-primary-300);
}

Scope your styles under a block-specific class (e.g., .my-block) to avoid conflicts with other blocks.

Do not use @import inside block style.css files. The framework inlines all block CSS into a single Tailwind entry via resources.Concat, so relative import paths will not resolve correctly through Hugo's virtual filesystem.

Manifest (Optional)

Add a manifest.json to provide metadata about your block:

{
  "id": "my-block",
  "name": "My Block",
  "version": "1.0.0",
  "license": "MIT",
  "category": "marketing",
  "tags": ["custom"],
  "description": "A custom block for my site",
  "author": "Your Name"
}

Schema Validation (Advanced)

Define a JSON Schema to enforce parameter types and defaults:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "properties": {
    "content": {
      "type": "object",
      "properties": {
        "title": { "type": "string" },
        "text": { "type": "string" }
      },
      "required": ["title"]
    }
  }
}

Schema validation is currently a developer-facing feature to help ensure data integrity.

Was this page helpful?

From the makers of

© 2026 Lore Labs.

On this page