> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockli.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Defining Blocks with the defineBlokkli() Compiler Macro

> Create Nuxt 3 block components using the defineBlokkli() compiler macro. Register bundle types, declare options, and control editor behavior.

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 path                           | Bundle identifier |
| ----------------------------------- | ----------------- |
| `components/Blokkli/Text.vue`       | `text`            |
| `components/Blokkli/HeroTeaser.vue` | `hero_teaser`     |
| `components/Blokkli/CardGrid.vue`   | `card_grid`       |

blökkli auto-registers all components in this directory — you don't need to import or declare them anywhere else.

<Note>
  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.
</Note>

## Basic Example

The minimal block component defines a bundle and uses `defineProps` to receive its data:

```vue theme={null}
<!-- 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:

```vue theme={null}
<!-- 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:

```vue theme={null}
<!-- 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.

<Tip>
  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.
</Tip>

## `defineBlokkli()` Parameters

<ParamField path="bundle" type="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.
</ParamField>

<ParamField path="options" type="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](/blocks/block-options) for the full type reference.
</ParamField>

<ParamField path="globalOptions" type="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.
</ParamField>

<ParamField path="editor" type="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.

  ```typescript theme={null}
  defineBlokkli({
    bundle: 'divider',
    editor: {
      disableEdit: true, // This block has no editable fields
    },
  })
  ```
</ParamField>

<ParamField path="chunkName" type="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.

  ```typescript theme={null}
  defineBlokkli({
    bundle: 'interactive_map',
    chunkName: 'heavy-blocks',
  })
  ```
</ParamField>

<ParamField path="renderFor" type="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.
</ParamField>

## Return Value

`defineBlokkli()` returns an object with a single property:

```typescript theme={null}
const { options } = defineBlokkli({ bundle: 'hero', options: { ... } })
```

| Property  | Type                    | Description                                                                                                                                                                   |
| --------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `options` | `Reactive<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. |

<Note>
  `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.
</Note>

## Full Example

Here is a complete block component combining props, local options, and global options:

```vue theme={null}
<!-- 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>
```
