How a block works

A Blökkli block is an ordinary Vue component with one extra call — defineBlokkli, its seven keys, why bundle must match the manifest, and how props become editor fields.

Blökkli is the block-based page builder behind revenexx themes. It is shipped and running live customer storefronts today.

This page is the reference for what a block is. The sibling pages cover the parts a block declares: options, fields and shells, inline editing, and registration. For the step-by-step walkthrough — writing your first block, wiring it to catalog data — follow Customize blocks.

A block is a Vue component

There is no block description language and no JSON block format you author. A block is an ordinary Vue 3 component with one extra call:

app/components/blokkli/badge/index.vue
<script setup lang="ts">
const props = defineProps<{
  label: string
}>()

const { options } = defineBlokkli({
  bundle: 'badge',
  options: {
    icon: {
      type: 'text',
      label: 'Icon',
      default: '',
      description: 'Solar icon token, e.g. bolt',
    },
  },
  editor: {
    mockProps: () => ({ label: 'B2B Supply Solutions' }),
  },
})
</script>

<template>
  <div class="badge">
    <UIcon v-if="options.icon" :name="options.icon" />
    <span v-blokkli-editable:label>{{ props.label }}</span>
  </div>
</template>

Two things to notice. The component's props become the editor's content fields — an editor filling in "Label" is setting props.label. And bundle is the identifier that ties this component to its entry in the theme manifest; get it out of sync and the block renders as unknown.

Props are content, options are settings

The distinction runs through everything else, so it's worth being precise about it.

PropsOptions
What they holdThe block's content — the headline, the body text, the product idThe block's settings — alignment, spacing, variant, width
Who sets themAn editor, typing into a field or directly on the canvasAn editor, turning a knob in the sidebar
How you declare themdefineProps<{ … }>(), as in any Vue componentThe options key on defineBlokkli
How you read themprops.headlinethe returned options object, which is reactive

If you find yourself putting a colour hex in a prop, it probably wants to be an option. If you find yourself putting a paragraph of copy in an option, it probably wants to be a prop.

Where blocks live

text
app/components/blokkli/<bundle>/index.vue

Naming is snake_case throughout — product_grid, accordion_item, account_title. A bundle with several related components keeps them in the same directory, with index.vue as the block itself.

defineBlokkli keys

bundle is the only required key. The other six are all optional, and each one is covered in depth on its own page.

KeyPurpose
bundleThe block's id. Must match its blokkli.blocks[].id in theme.json.
optionsEditor settings for this block. Returned as a reactive options object. See Block options.
globalOptionsNames of options shared across blocks, merged into options. See Block options.
propsFieldMappingDeclares that a prop is a nested field, an editable region, or a drop target rather than a plain value. See Shells and fields.
renderForRestricts where the component is used, so one bundle can render differently by parent, field, or provider. See Inline editing.
editorEditor-only behaviour: icon, mockProps, previewWidth, maxInstances, editTitle, fieldLayout, and friends. None of it ships to the live page.
chunkNameAssigns the component to a named import chunk.

editor.mockProps is the one to reach for early: it gives the editor something to render before real content exists, so a freshly dragged block does not look broken. See Inline editing.

bundle ties three things together

The single most common failure — a block rendering as "unknown" — is always the same cause: three strings that must match don't.

  1. The component's bundle in defineBlokkli.
  2. The id in blokkli.blocks[] in theme.json.
  3. The key in the theme's block-id-to-component map.

Align all three and the block appears. Details in Registering blocks.

One requirement on the theme

The editor anchors to the element with id nuxt-root. Nuxt's default root id is __nuxt, so a theme must rename it or the editor cannot attach:

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    rootId: 'nuxt-root',
  },
})

Themes extending @revenexx/cover-theme inherit this. If you build directly on @revenexx/cover, set it yourself.

Next steps

Was this page helpful?