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

# Blocks Field

> Build ordered, flexible content from reusable block schemas.

The Blocks field stores an ordered array in which each item can use a different schema. It is useful for page builders, forms, and other content that mixes reusable structures.

Unlike an Array field, where every row has the same shape, a Blocks field lets editors mix configured block types in any order. Common uses include:

* Page builders with quote, call-to-action, slider, and gallery blocks
* Form builders with text, select, and checkbox blocks
* Event agendas with break, presentation, and breakout-session blocks

```ts theme={"theme":"tokyo-night"}
import type { Block, BlocksField } from 'frogbot'

const QuoteBlock: Block = {
  slug: 'quote',
  interfaceName: 'QuoteBlock',
  fields: [
    {
      name: 'quote',
      type: 'textarea',
      required: true,
    },
    {
      name: 'attribution',
      type: 'text',
    },
  ],
}

export const layoutField: BlocksField = {
  name: 'layout',
  type: 'blocks',
  blocks: [QuoteBlock],
  minRows: 1,
  maxRows: 20,
}
```

Each stored item includes `blockType`, whose value is the block's `slug`, and an optional editor-controlled `blockName`.

## Field config options

| Option | Type | Default | Description |
| - | - | - | - |
| `name` | `string` | Required | Property name used in stored records and APIs. See [field names](https://docs.frogbot.ai/fields/overview.md#field-names). |
| `type` | `'blocks'` | Required | Selects the Blocks field. |
| `blocks` | `(Block \| string)[]` | Required | Blocks available to this field: block configs, or slugs of blocks registered in the root `blocks` config. See [Root block registration](#root-block-registration). |
| `filterOptions` | `string[] \| ({ id, data, req, siblingData, user }) => string[] \| true \| Promise<string[] \| true>` | All blocks | Block slugs available for the current document; `true` allows every block. See [Conditional blocks](#conditional-blocks). |
| `labels` | `{ singular, plural }` | Generated from `name` | Singular and plural labels for block rows. Each accepts the same values as `label`, except `false`. |
| `minRows` | `number` | — | Minimum number of blocks when a value is present. |
| `maxRows` | `number` | — | Maximum number of blocks when a value is present. |
| `label` | `string \| Record<string, string> \| (({ i18n, t }) => string) \| false` | Generated from `name` | Label shown in the admin panel. Use an object keyed by language code for translations, or `false` to hide the label. |
| `required` | `boolean` | `false` | Rejects an empty value in the default validation. |
| `unique` | `boolean` | `false` | MongoDB only: creates a unique index on the whole stored value. SQL adapters ignore it. |
| `index` | `boolean` | `false` | MongoDB only: creates an index on the whole stored value. SQL adapters ignore it. |
| `localized` | `boolean` | `false` | Stores a separate block array for each configured locale. Ignored without [localization](https://docs.frogbot.ai/configuration/localization.md) or inside a localized parent. |
| `defaultValue` | `object[]` or `({ locale, req, user }) => value` | — | Value used when the field has no value. See [default values](https://docs.frogbot.ai/fields/overview.md#default-values). |
| `validate` | `Validate` | Built-in validator | Replaces the built-in validator. See [validation](https://docs.frogbot.ai/fields/overview.md#validation). |
| `hooks` | `{ beforeValidate, beforeChange, afterChange, afterRead, beforeDuplicate }` | — | Arrays of `FieldHook` functions for each phase. See [field hooks](https://docs.frogbot.ai/hooks/fields.md). |
| `access` | `{ create, read, update }` | Allowed | `FieldAccess` functions for each operation. See [field access control](https://docs.frogbot.ai/access-control/fields.md). |
| `hidden` | `boolean` | `false` | Removes the field from API responses and the admin panel while keeping stored data. |
| `saveToJWT` | `boolean \| string` | `false` | In an auth-enabled collection, copies the value into the user's token. A string sets the token key. See [token data](https://docs.frogbot.ai/authentication/token-data.md). |
| `virtual` | `boolean \| string` | `false` | `true` skips database storage; a string reads a value through a relationship path. Virtual fields are read-only in the admin panel unless `admin.readOnly` is `false`. See [virtual fields](https://docs.frogbot.ai/fields/overview.md#virtual-field-configuration). |
| `disableDuplicate` | `boolean` | `false` | Leaves the value out when a document is duplicated, so the copy uses `defaultValue`. |
| `admin` | `object` | — | Admin-panel options. See [Field admin options](#field-admin-options). |
| `custom` | `Record<string, any>` | — | Server-only metadata for your own code and plugins. Use `admin.custom` for data the browser also needs. |
| `jsonSchema` | `Array<({ jsonSchema }) => JSONSchema4>` | — | Functions that modify the JSON Schema used by [type generation](https://docs.frogbot.ai/typescript/generating-types.md). |

Setting `unique: true` on a field inside a block creates a collection-wide
database unique index, not uniqueness within one document. Use [custom
validation](https://docs.frogbot.ai/fields/overview.md#validation) on the Blocks field to reject
duplicate values among its blocks.

## Field admin options

Blocks fields inherit the standard [field admin options](https://docs.frogbot.ai/fields/overview.md#admin-options) and add:

| Option | Type | Default | Description |
| - | - | - | - |
| `initCollapsed` | `boolean` | `false` | Collapses each block row initially. An editor's saved row state takes precedence. |
| `isSortable` | `boolean` | `true` | Set to `false` to prevent block reordering. |

## Copying and pasting blocks

The admin panel can copy and paste individual block rows or the complete contents of a Blocks field. These actions are available from each row's action menu and the field-level action menu.

### Row-level and field-level actions

* **Copy Row** copies one block and lets you paste it at a chosen position in a compatible Blocks field.
* **Copy Fields** copies every block in the field.
* **Duplicate** immediately inserts a copy below the original block in the same field and document.
* **Paste Fields** replaces the target field's entire contents with the clipboard contents.

Copy and paste can move blocks between compatible fields and documents in browser tabs on the same origin. Clipboard data is kept in browser storage and is not shared between different FrogBot origins. FrogBot checks that the target field includes the copied block type and regenerates block and nested Array row IDs when pasting.

**Paste Fields replaces every existing block**, even when the clipboard holds
only one row. To add one copied block without replacing the field, add an
empty row and use **Paste Row** from that row's action menu.

## Block items

Blocks are separate, reusable configuration objects. Define block objects outside collection definitions when multiple fields or collections use the same schema.

Keep reusable blocks in separate files. One block object can then be shared
by Blocks fields and Rich Text editors without duplicating its schema.

## Block config options

| Option | Type | Default | Description |
| - | - | - | - |
| `slug` | `string` | Required | Identifier stored as `blockType`. Must be unique within the field. |
| `fields` | `Field[]` | Required | Fields stored in this block. |
| `labels` | `{ singular, plural }` | Generated from `slug` | Singular and plural labels shared by every instance of this block type. Each accepts the same values as `label`, except `false`. |
| `interfaceName` | `string` | — | Name of a reusable top-level generated TypeScript interface. |
| `dbName` | `string \| ({ tableName }) => string` | Generated from the slug | Table name used by SQL adapters. |
| `admin` | `object` | — | Block display, grouping, images, and component settings. See [Block admin options](#block-admin-options). |
| `custom` | `Record<string, any>` | — | Server-only metadata for your own code and plugins. |
| `jsx` | `{ export, import, customStartRegex?, customEndRegex?, doNotTrimChildren? }` | — | Converts this block to and from MDX when Rich Text content is converted to or from Markdown. See [Converting MDX](https://docs.frogbot.ai/rich-text/converting-markdown.md#converting-mdx). |
| `graphQL.singularName` | `string` | — | Deprecated. Schema name used only when an application mounts its own GraphQL executor. Use `interfaceName`. |
| `imageURL` | `string` | — | Deprecated. Thumbnail URL for the block-selection drawer. Use `admin.images.thumbnail`. |
| `imageAltText` | `string` | — | Deprecated. Alternative text for `imageURL`. Use `admin.images.thumbnail.alt`. |

### Block admin options

Blocks are configuration objects rather than fields, so their admin options are independent of the standard field admin options:

| Option | Type | Default | Description |
| - | - | - | - |
| `components.Block` | `FrogBotComponent` | — | Replaces the complete block UI, including its header. |
| `components.Label` | `FrogBotComponent` | — | Replaces the shared label and each row's displayed name. |
| `disableBlockName` | `boolean` | `false` | Hides the editable `blockName` control. |
| `group` | `string \| Record<string, string>` | — | Groups this block under a heading in the block-selection drawer. |
| `images` | `{ icon?, thumbnail? }` | Default graphics | Icon and thumbnail images. Each accepts a URL string or `{ url, alt? }`. See [Block images](#block-images). |
| `jsx` | `FrogBotComponent` | — | Import path to the block's `jsx` converter, used when Markdown conversion runs in the browser. |
| `custom` | `Record<string, any>` | — | Metadata available on the server and in the browser. |

### Block images

`admin.images.icon` appears in Rich Text menus and toolbars at 20 by 20 pixels. Square SVG or transparent PNG assets work best. If no icon is configured, the thumbnail is used as a fallback.

`admin.images.thumbnail` appears in the block-selection drawer at a 3:2 aspect ratio. Other ratios are cropped with `object-fit: cover`, so keep important content centered. Each image accepts either a URL string or an object containing `url` and optional `alt` text.

```ts theme={"theme":"tokyo-night"}
import type { Block } from 'frogbot'

export const QuoteBlock: Block = {
  slug: 'quote',
  admin: {
    images: {
      icon: {
        url: '/block-images/quote-icon.svg',
        alt: 'Quote',
      },
      thumbnail: {
        url: '/block-images/quote-thumbnail.webp',
        alt: 'A quotation with an attribution',
      },
    },
  },
  fields: [
    {
      name: 'quote',
      type: 'textarea',
      required: true,
    },
  ],
}
```

URL strings are useful when separate alternative text is unnecessary:

```ts theme={"theme":"tokyo-night"}
import type { Block } from 'frogbot'

export const DividerBlock: Block = {
  slug: 'divider',
  admin: {
    images: {
      icon: '/block-images/divider-icon.svg',
    },
  },
  fields: [],
}
```

Both images can also use URL strings:

```ts theme={"theme":"tokyo-night"}
import type { Block } from 'frogbot'

export const CallToActionBlock: Block = {
  slug: 'callToAction',
  admin: {
    images: {
      icon: '/block-images/call-to-action-icon.svg',
      thumbnail: '/block-images/call-to-action-thumbnail.webp',
    },
  },
  fields: [
    {
      name: 'buttonText',
      type: 'text',
    },
  ],
}
```

When `images` is omitted, the admin panel supplies default graphics.

### Block type, name, and label

Each block row stores `blockType` and may store `blockName`. The block config's `labels` apply to every block of that type, while `blockName` belongs to one row.

| Property | Scope | Source | Admin behavior |
| - | - | - | - |
| **`blockType`** | One row | Block `slug` | Resolves the schema and is not the editable header. |
| **`blockName`** | One row | Editor input | Optional row name; hide it with `admin.disableBlockName`. |
| **Block label** | Block type | `labels` or the `slug` fallback | Shared by every row of that type. |

`admin.components.Label` replaces both the configured block label and the editable row-name presentation.

## Root block registration

Register a reusable block once in the root config, then reference it by slug. Object references are also accepted.

```ts theme={"theme":"tokyo-night"}
import { buildConfig } from 'frogbot'
import type { Block } from 'frogbot'

const TextBlock: Block = {
  slug: 'text',
  fields: [
    {
      name: 'text',
      type: 'textarea',
    },
  ],
}

export default buildConfig({
  blocks: [TextBlock],
  collections: [
    {
      slug: 'pages',
      fields: [
        {
          name: 'layout',
          type: 'blocks',
          blocks: ['text'],
        },
      ],
    },
  ],
})
```

Referenced block configuration is shared and cannot be extended at the field. Its access control runs without collection-specific sibling data.

The root registration prevents the same block schema from being sent and processed repeatedly when many fields reuse it.

Referenced blocks are isolated from the collection configuration. Their
schema cannot be extended at the reference site, and their access control is
evaluated without collection-specific data.

## TypeScript

Use the public `Block` type for separately authored block objects and `BlocksField` for complete field objects:

```ts theme={"theme":"tokyo-night"}
import type { Block, BlocksField } from 'frogbot'

export const HeroBlock: Block = {
  slug: 'hero',
  interfaceName: 'HeroBlock',
  fields: [
    {
      name: 'heading',
      type: 'text',
      required: true,
    },
  ],
}

export const heroLayout: BlocksField = {
  name: 'layout',
  type: 'blocks',
  blocks: [HeroBlock],
}
```

`interfaceName` gives the block a reusable top-level interface when you run `frogbot generate:types`.

## Conditional blocks

`filterOptions` can return `true` for all configured blocks or an array of allowed slugs. It is reevaluated when form state changes, and saving fails if an existing block is no longer allowed. A callback receives `data`, `id`, `siblingData`, `user`, and `req`, the current `FrogBotRequest` with `req.frogbot`.

```ts theme={"theme":"tokyo-night"}
import type { BlocksField } from 'frogbot'

export const conditionalLayout: BlocksField = {
  name: 'layout',
  type: 'blocks',
  blocks: [
    { slug: 'hero', fields: [{ name: 'heading', type: 'text' }] },
    { slug: 'quote', fields: [{ name: 'quote', type: 'textarea' }] },
  ],
  filterOptions: ({ siblingData }) => {
    return siblingData.mode === 'simple' ? ['hero'] : true
  },
}
```

## Lexical blocks

The same FrogBot `Block` objects can be embedded in a Lexical Rich Text field with `BlocksFeature`:

```ts theme={"theme":"tokyo-night"}
import { BlocksFeature, lexicalEditor } from '@frogbotai/richtext-lexical'
import type { Block, RichTextField } from 'frogbot'

const CalloutBlock: Block = {
  slug: 'callout',
  fields: [
    {
      name: 'message',
      type: 'textarea',
      required: true,
    },
  ],
}

export const contentField: RichTextField = {
  name: 'content',
  type: 'richText',
  editor: lexicalEditor({
    features: ({ defaultFeatures }) => [
      ...defaultFeatures,
      BlocksFeature({ blocks: [CalloutBlock] }),
    ],
  }),
}
```

Nested Rich Text fields need their own `editor`. See the [Rich Text field](https://docs.frogbot.ai/fields/rich-text.md) for editor configuration.

### Custom Lexical rendering

A block's `admin.components.Block` replaces its preview inside the Lexical editor, and `admin.components.Label` replaces its label. Server previews use `LexicalBlockServerProps`; client previews use `LexicalBlockClientProps`.

The `Block` type also preserves `jsx` conversion metadata for compatible Rich Text rendering pipelines. Configure it on the reusable block rather than creating a second schema for rendering.

Client previews can reuse the editor's public controls instead of rebuilding edit and remove behavior:

```tsx theme={"theme":"tokyo-night"}
'use client'

import {
  BlockCollapsible,
  BlockEditButton,
  BlockRemoveButton,
  InlineBlockContainer,
  InlineBlockEditButton,
  InlineBlockLabel,
  InlineBlockRemoveButton,
} from '@frogbotai/richtext-lexical/client'
```

Use the regular block controls for block previews and the inline controls for inline block previews. `BlockCollapsible` and `InlineBlockContainer` provide the corresponding default containers.

## Custom components

Replace the Blocks field while retaining the default UI:

```tsx theme={"theme":"tokyo-night"}
import { BlocksField } from '@frogbotai/ui/admin'
import type { BlocksFieldServerProps } from 'frogbot'

export const CustomBlocksField = ({
  clientField,
  path,
  permissions,
  schemaPath,
}: BlocksFieldServerProps) => {
  return (
    <BlocksField
      field={clientField}
      path={path}
      permissions={permissions}
      schemaPath={schemaPath}
    />
  )
}
```

Client field replacements receive props compatible with the default field component:

```tsx theme={"theme":"tokyo-night"}
'use client'

import { BlocksField } from '@frogbotai/ui/admin'
import type { BlocksFieldClientProps } from 'frogbot'

export const CustomBlocksFieldClient = (props: BlocksFieldClientProps) => {
  return <BlocksField {...props} />
}
```

Replace only the field label when the default Blocks interface should remain unchanged:

```tsx theme={"theme":"tokyo-night"}
import { FieldLabel } from '@frogbotai/ui/admin'
import type { BlocksFieldLabelServerProps } from 'frogbot'

export const CustomBlocksLabel = ({
  clientField,
  path,
}: BlocksFieldLabelServerProps) => {
  return (
    <FieldLabel
      label={clientField?.label || clientField?.name}
      path={path}
      required={clientField?.required}
    />
  )
}
```

Use `BlocksFieldLabelClientProps` for a client label and `BlockRowLabelServerProps` or `BlockRowLabelClientProps` for block rows.

Lexical block previews use `LexicalBlockServerProps` or `LexicalBlockClientProps` from `@frogbotai/richtext-lexical`. Configure their component paths on the block's `admin.components.Block` slot.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.