---
title: "Custom Block"
description: "Re-use existing core blocks and build custom blocks that allow developers to tailor the email builder to their specific needs and requirements of users."
url: https://docs.topol.io/email-editor/guide/custom-block.html
---

# Custom Content Block

> **This feature is available with the Plugin for Business plan and higher.**
>
> If you're on the Plugin for Startup or Plugin Expansion plan, consider upgrading to access this feature. For more details, visit our [pricing page](https://topol.io/pricing) or contact support.

**Custom blocks add your own entries to the editor's block sidebar,** built either from the core block types or from raw HTML. They cover three common needs:

-   **Embed custom HTML or functionality**
    Add unique design elements or interactivity that are not available via standard blocks.

-   **Align with your brand**
    Create consistent components that match your company's design language and marketing standards.

-   **Improve productivity**
    Reuse common layouts or design sections without repetitive setup, which suits newsletters, headers and CTAs.


Only `key` and `name` are required on a custom block. Everything else is optional.

## Creating a basic custom block

A custom block can be based on an existing core type: `text`, `button`, `image` or `video`. Define it in the `customBlocks` array inside `TOPOL_OPTIONS` and set `type` to the core block it builds on.

```ts
customBlocks: [
{
  key: "custom-text-key", // unique ID
  name: "My Customised Text", // visible name in sidebar
  icon: `<svg style="width: 40px; height: 40px;" fill="currentColor" viewBox="0 0 20 20">...</svg>`, // SVG icon
  disabled: false,
  type: "text", // must match a valid Topol block type
  attributes: { // for more details, see INFO section below
    align: "right",
    padding: "10px 10px",
    "line-height": 1.3,
    "css-class": "hide_on_mobile"
  },
  content: "Custom Text block?"
}
]
```

> **INFO**
>
> Every attribute that exists on a block type can be defined here. The full list is in the [block reference](https://docs.topol.io/email-editor/template-reference/blocks/text.html#topol-text-block).

Attributes that fail the target block's schema are dropped silently rather than rejecting the block, so it is worth checking the reference above when an attribute does not take effect.

### Choosing an icon

The `icon` property accepts three forms:

-   **`box` or `rss`**, the two built-in glyphs.
-   **An inline SVG string**, which has to start with `<svg` and end with `</svg>`. A fragment that fails either check falls back to the box glyph.
-   **An `https://` image URL**, rendered as an image.

## Composing multiple blocks

Several blocks can be combined into a single drag-and-drop unit with the `mix` type and a `blocks` array. The example below combines `text` and `button`, so dragging this custom block adds both elements at once. Inside `blocks`, `type` is required on every entry.

```ts
customBlocks: [
{
  key: "custom-mix",
  name: "Custom mixed block",
  icon: "box",
  disabled: false,
  type: "mix",
  blocks: [
    {
      type: "text",
      attributes: {
        align: "left",
      },
      content: "My custom content",
    },
    {
      type: "button",
      attributes: {
        align: "center",
        "background-color": "#417505",
        color: "#F8E71C",
      },
      content: "Click me!",
    },
  ],
},
]
```

## Modifying and restricting attributes

Control and limit which settings can be changed by the user. In the example below, we disabled background color changing and set a default value for the block.

```ts
customBlocks: [
{
  key: "custom-text-with-modifier",
  name: "My Customised Text",
  icon: "box",
  type: "button",
  attributes: {
    "font-size": "20px",
    "background-color": {
      value: "#417505",
      disabled: true,
    },
    href: {
      value: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    },
  },
  content: "Watch the video",
},
]
```

## Marking a block as disabled or beta

`disabled: true` greys the block out, removes its drag handle and ignores clicks, which suits gating a block behind your own entitlement checks. `disabledBadge` sets the label shown on it, and `beta` adds a beta badge to a block that stays usable.

```ts
customBlocks: [
{
  key: "custom-premium",
  name: "Premium layout",
  icon: "box",
  type: "text",
  disabled: true,
  disabledBadge: "Pro only",
  beta: false,
  content: "Upgrade to use this block",
},
]
```

## Using custom HTML with a custom dialog

For complete control over the block's structure and interactivity, **omit `type` entirely** and set `dialog: true`. The block then carries raw HTML, edited through a dialog you provide.

> **WARNING**
>
> We want to clarify that we cannot assume responsibility for the content, security, or performance of custom HTML code that originates outside our official codebase. Users are advised to exercise caution and follow coding best practices when incorporating custom HTML, ensuring compliance with relevant standards and security measures.

```ts
customBlocks: [
  {
    key: "custom-block-key",
    name: "My Custom Block", //displayed in menu
    dialog: true, //edit content in dialog (currently only custom dialog)
    icon: "box", // box and rss are built in, or pass an SVG string or an https:// image URL
    content: "<p>initial content</p>", // custom HTML code
    dialogButtonText: "Open Custom Dialog",
    disabled: false, //disabled button for certian users
  },
];
```

In the example above, we provided the text "Open Custom Dialog" for the button. When a user clicks the button, the `onOpenCustomBlockDialog()` callback is fired:

```ts
callbacks: {
  onOpenCustomBlockDialog(customBlock) {
    // Open your custom dialog
    // Retreive edited data
    // Access current content via customBlock.content
    const editedContent = prompt("Edit HTML content:", customBlock.content);

    // Once the dialog is closed, we can update the current block using following function
    TopolPlugin.updateCustomBlockContent(editedContent);
  }
}
```

`customBlock.content` carries the content of the placed block as it currently stands, rather than the default from your configuration, so reopening the dialog gives back the user's latest edit.
