Registering blocks

Making a block usable — the blokkli.blocks[] and blokkli.presets[] arrays in theme.json, the block-id-to-component map, and gating a block behind requiresCapability.

Writing the component is half the job. A block becomes usable when the manifest declares it and the theme can resolve its id to a component.

blokkli.blocks[]

A block appears in the editor's block list once it's in the theme manifest, keyed by the same id as the component's bundle:

theme.json
{
  "blokkli": {
    "blocks": [
      { "id": "badge", "title": "Badge", "description": "Small inline label with an optional icon." },
      { "id": "product_grid", "title": "Product Grid", "description": "Renders products in a responsive grid.", "requiresCapability": "products.list" }
    ]
  }
}
FieldWhat it does
idThe block's identifier. Must equal the component's bundle.
titleWhat an editor sees in the block picker. Write it for the editor, not the developer.
descriptionThe one-line explanation shown alongside the title. Say what the block produces, not how it's implemented.
requiresCapabilityGates the block on a platform capability — see below.

title and description are the block's user interface as far as an editor is concerned. "Product Grid — renders products in a responsive grid" tells them something; "ProductGridBlock (v2)" does not.

The block-id-to-component map

The manifest tells the platform the block exists. The theme also needs to tell its renderer which component to instantiate:

app/config/blokkliBlocks.ts
import type { Component } from 'vue'

import BlokkliBadge from '../components/blokkli/badge/index.vue'
import BlokkliProductGrid from '../components/blokkli/product_grid/index.vue'
import BlokkliText from '../components/blokkli/text/index.vue'

export const BLOCK_COMPONENTS: Record<string, Component> = {
  badge: BlokkliBadge,
  product_grid: BlokkliProductGrid,
  text: BlokkliText,
}

So three strings must agree for every block:

  1. defineBlokkli({ bundle: 'product_grid' }) in the component
  2. { "id": "product_grid" } in theme.json
  3. product_grid: in the block map

Drift between them is the single most common reason a block renders as "unknown." When that happens, check all three before debugging anything else.

requiresCapability

A block that reads platform data can't function on a tenant that doesn't have the providing app installed. requiresCapability makes that explicit:

theme.json
{ "id": "product_grid", "title": "Product Grid", "requiresCapability": "products.list" }

The effect is per-block rather than per-theme: on a tenant without products.list, the theme still installs and every other block still works — the product grid simply isn't offered in the editor. An editor never places a block that would render an error.

That's different from the manifest's top-level requires[], which is a hard install dependency: a theme that requires a capability cannot be installed at all without it. Use requires[] when the theme is pointless without the capability, and requiresCapability when one block is.

theme.json
{
  "requires": [
    {
      "capability": "products.list",
      "compatible": "^1.0",
      "reason": "renders the product grid on the home and category pages"
    }
  ]
}

More on both in Theme anatomy.

blokkli.presets[]

A preset is a starting arrangement an editor drops in as a unit, rather than assembling a page block by block:

theme.json
{
  "blokkli": {
    "presets": [
      { "id": "home", "title": "Home page", "blocks": ["marketing_banner", "content_section", "product_swiper", "text", "product_grid"] },
      { "id": "landing", "title": "Landing page", "blocks": ["hero", "text", "image", "cta"] },
      { "id": "faq", "title": "FAQ page", "blocks": ["hero", "accordion"] }
    ]
  }
}

blocks is an ordered list of bundle ids — the arrangement the preset stamps out. Presets are cheap to add and they do a lot of work on a real project: a customer's content team creating their fifth landing page should not have to remember which five blocks the last four used.

Keep them few and genuinely different. Three presets that mean something beat nine that overlap.

When registration takes effect

The block map is code, so it ships with the deployment. The manifest is read by the platform when the theme is registered, which happens on publish — so a new block in blokkli.blocks[] reaches the editor when you publish a new theme version, not merely when you deploy.

And because a published version is immutable, that means bumping theme.json#version. See Publishing a theme.

Next steps

Was this page helpful?