Skip to main content
Every content block in blökkli is a Vue single-file component (SFC) that calls defineBlokkli() inside its <script setup> block. blökkli uses the bundle identifier you provide to match incoming block data to the correct component — think of it as the contract between your backend data model and your frontend rendering logic.

File Naming Convention

Block components live in the components/Blokkli/ directory and are named after their bundle using PascalCase:
File pathBundle identifier
components/Blokkli/Text.vuetext
components/Blokkli/HeroTeaser.vuehero_teaser
components/Blokkli/CardGrid.vuecard_grid
blökkli auto-registers all components in this directory — you don’t need to import or declare them anywhere else.
The PascalCase-to-snake_case conversion is automatic. MyBlock.vue maps to the bundle my_block. Make sure your backend returns bundle strings that match this convention.

Basic Example

The minimal block component defines a bundle and uses defineProps to receive its data:
<!-- components/Blokkli/Text.vue -->
<template>
  <div>
    <p>{{ text }}</p>
  </div>
</template>

<script lang="ts" setup>
const props = defineProps<{
  text: string
}>()

defineBlokkli({
  bundle: 'text',
})
</script>
blökkli passes all fields from the block data object as props automatically. Your defineProps declaration should match the shape of the data your backend returns for that bundle.

With Options

Options let content editors customise a block’s appearance and behaviour directly from the editor panel. Define them in defineBlokkli() and destructure the reactive options object from the return value:
<!-- components/Blokkli/Hero.vue -->
<template>
  <section :class="['hero', `hero--${options.layout}`]">
    <h1>{{ title }}</h1>
    <p v-if="options.showSubtitle">{{ subtitle }}</p>
  </section>
</template>

<script lang="ts" setup>
defineProps<{
  title: string
  subtitle: string
}>()

const { options } = defineBlokkli({
  bundle: 'hero',
  options: {
    layout: {
      type: 'radios',
      label: 'Layout',
      default: 'centered',
      options: {
        centered: 'Centered',
        left: 'Left aligned',
        fullwidth: 'Full width',
      },
    },
    showSubtitle: {
      type: 'checkbox',
      label: 'Show subtitle',
      default: true,
    },
  },
})
</script>
options is fully reactive — when a content editor changes a value in the editor panel, the component re-renders immediately without a page reload.

With Global Options

Global options are reusable option definitions you declare once in nuxt.config.ts and then attach to any block. This is useful for site-wide concerns like spacing scales or colour schemes:
<!-- components/Blokkli/Card.vue -->
<script lang="ts" setup>
const { options } = defineBlokkli({
  bundle: 'card',
  globalOptions: ['spacing', 'colorScheme'], // defined in nuxt.config.ts
  options: {
    // Local options specific to this block
    showImage: {
      type: 'checkbox',
      label: 'Show image',
      default: true,
    },
  },
})
</script>
The returned options object merges both global and local values, so you access them all through the same reactive reference.
Define shared presentation options like spacing, colorScheme, or containerWidth as global options. This keeps individual block definitions lean and ensures a consistent editor experience across all blocks.

defineBlokkli() Parameters

bundle
string
required
Unique identifier for the block type. Must match the bundle string returned by your backend data source. blökkli uses this value to look up the correct component at render time.
options
object
Block-level options object. Each key is an option name and each value is an option definition describing the input type, label, and default value. Options appear in the editor’s options panel for this block. See Block Options for the full type reference.
globalOptions
string[]
Array of global option keys to include for this block. Global options are declared once in nuxt.config.ts under blokkli.globalOptions and shared across many blocks. The values are merged into the returned options object alongside any local options.
editor
object
Editor behaviour overrides for this block. Use this to control how the block behaves inside the editor UI. For example, passing { disableEdit: true } prevents the edit form from opening when a content editor clicks on the block.
defineBlokkli({
  bundle: 'divider',
  editor: {
    disableEdit: true, // This block has no editable fields
  },
})
chunkName
string
Groups this block component into a named async chunk for code splitting. Blocks that share the same chunkName are bundled together. Use this to optimise loading performance when you have many block types — group rarely-used blocks into a separate chunk so they don’t inflate your initial bundle.
defineBlokkli({
  bundle: 'interactive_map',
  chunkName: 'heavy-blocks',
})
renderFor
array
Constraint array controlling when this block component is rendered. Use it to restrict a block to specific entity types or bundles — for example, rendering a different variant of a block only within a particular page type.

Return Value

defineBlokkli() returns an object with a single property:
const { options } = defineBlokkli({ bundle: 'hero', options: { ... } })
PropertyTypeDescription
optionsReactive<YourOptions>A reactive object containing the current values of all defined options (both local and global). Values update live when the content editor makes changes in the editor panel.
options is always reactive, even if you haven’t defined any options. In that case it resolves to an empty reactive object. You only need to destructure it when you actually use options in your template or script.

Full Example

Here is a complete block component combining props, local options, and global options:
<!-- components/Blokkli/FeatureList.vue -->
<template>
  <section
    :class="[
      'feature-list',
      `feature-list--cols-${options.columns}`,
      `spacing--${options.spacing}`,
    ]"
  >
    <h2 v-if="heading">{{ heading }}</h2>
    <ul>
      <li v-for="item in items" :key="item.id">
        <img v-if="options.showIcons && item.icon" :src="item.icon" alt="" />
        <span>{{ item.label }}</span>
      </li>
    </ul>
  </section>
</template>

<script lang="ts" setup>
defineProps<{
  heading: string
  items: Array<{ id: string; label: string; icon?: string }>
}>()

const { options } = defineBlokkli({
  bundle: 'feature_list',
  globalOptions: ['spacing'],
  options: {
    columns: {
      type: 'radios',
      label: 'Columns',
      default: '3',
      options: { '2': '2', '3': '3', '4': '4' },
    },
    showIcons: {
      type: 'checkbox',
      label: 'Show icons',
      default: true,
    },
  },
})
</script>