Skip to content

Saved Blocks and Synced Sections

Topol provides two powerful features for reusing content across your email templates:

  • Saved Blocks - Save any section or content block and reuse it across multiple templates. When you insert a saved block, it creates a copy that you can edit independently.

  • Synced Sections - Save sections that stay synchronized across all templates. When you edit a synced section, the changes are automatically reflected everywhere it's used.

Enabling Saved Blocks

Users can save their structures by clicking "Save as Block" button while the structure they want to save is selected.

Save as block

To enable this feature, set savedBlocks to an empty array.

ts
savedBlocks: [],

To disable it, set savedBlocks to null.

ts
savedBlocks: null,

You can also hide saved blocks by setting savedBlocks to false.

ts
savedBlocks: false,

Each saved block within the savedBlocks array is an object and has the following structure:

ts
{
  id: 1, // ID of the block, if not set it uses the Array Index
  name: 'My saved block 001', // You can set a name or an image
  img: 'http://...', // The editor shows only name or image, not both, if both set it will show the image
  definition: 'json' // JSON of this saved block
},

Enabling Synced Sections

To enable synced sections, set syncedSectionsEnabled to true:

ts
syncedSectionsEnabled: true,

When synced sections are enabled, users can save structures as synced by clicking "Save as Synced structure" in the section actions menu. Unlike regular saved blocks, synced sections maintain a live connection to all templates where they're used - editing a synced section updates it everywhere.

Advanced Configuration

There are three actions you need to implement to make saved blocks work:

  • onBlockSave
  • onBlockEdit
  • onBlockRemove

All of them needs to be implemented in the callbacks object.

Example:

ts
callbacks: {
  onBlockSave(json) {
    const name = window.prompt('Enter block name:')
    if (name !== null) {
      console.log('saving block', json)
    }
  },

  onBlockRemove(id) {
    if (window.confirm('Are you sure?')) {
      console.log('removing block', id)
    }
  },

  onBlockEdit(id) {
    const name = window.prompt('Block name:', 'My block 001')
    if (name !== null) {
      console.log('saving edited block', id)
    }
  }
}

Since it's your responsibility to handle those actions, you also need to update the list of saved blocks once they are updated. To do this, you can use the TopolPlugin.setSavedBlocks([savedBlocks]) function.

This function takes an array of all saved blocks you want to display in the editor.

ts
TopolPlugin.setSavedBlocks([
  {
    id: 1, // ID of the block, if not set it uses the Array Index
    name: "My saved block 001", // You can set a name or an image
    img: "http://...", // The editor shows only name or image, not both, if both set it will show the image
    definition: "json", // JSON of this saved block
  },
]);

API-Based Configuration

For a more advanced setup, you can use the API-based feature. This allows you to store saved blocks and synced sections on your server with support for folders, search, pagination, and preview images.

To enable API-based saved blocks and synced sections, set savedBlocks to true and configure the api.SAVED_SECTIONS endpoint:

ts
const TOPOL_OPTIONS = {
  savedBlocks: true,
  syncedSectionsEnabled: true, // Enable synced sections
  api: {
    SAVED_SECTIONS: "https://your-domain.com/saved-blocks",
  },
};

Both saved blocks and synced sections use the same API.SAVED_SECTIONS endpoint. They are differentiated by the type field in the API responses and requests.

Before implementing the endpoints check how to work with API endpoints

List Saved Blocks, Synced Sections and Folders

Request

  • URL: /{API.SAVED_SECTIONS}
  • Method: GET
  • Content-Type: application-json

Params:

keyvaluerequired
keyauthorization api keytrue
hostnamehostnametrue
entity_identity_id corresponds to userId in Optionstrue
per_pagenumber of items per pagefalse
current_pagenumber of page to returnfalse
sort_by"name" or "date" or "type"false
desc"true"false
folder_idid of folder to returnfalse
searchstring to searchfalse
type"saved_section" or "synced_section" to filter by typefalse

This endpoint is called when the editor lists all saved blocks/synced sections, changes sort, page or folder, searches by string, or when you call TopolPlugin.refreshSyncedSections().

Response

json
{
  "success": true,
  "data": {
    "data": [
      {
        "id": 1,
        "name": "name of saved block, synced section, or folder",
        "type": "saved_section" or "synced_section" or "folder",
        "created_at": "ISO 8601 time",
        "definition": "json of MJML section",
        "image": "https://url-to-preview-image.jpg",
        "folder_id": 2
      }
    ],
    "lastPage": 5,
    "parentFolderId": null
  }
}

Create Saved Block, Synced Section or Folder

  • URL: /{API.SAVED_SECTIONS}
  • Method: POST

This endpoint is called when a new saved block, synced section, or folder is created.

Request

json
{
  "entity_id": "entity user_id",
  "hostname": "origin",
  "key": "api key",
  "type": "saved_section" or "synced_section" or "folder",
  "name": "name of new item",
  "definition": "json of MJML section",
  "folder_id": 1
}

Response

STATUS 200

json
{
  "success": true,
  "data": {
    "id": 2,
    "name": "name of saved block, synced section, or folder",
    "type": "saved_section" or "synced_section" or "folder",
    "created_at": "ISO 8601 time",
    "definition": "json of MJML section",
    "folder_id": 1
  }
}

Get Saved Block or Synced Section Detail

Request

  • URL: /{API.SAVED_SECTIONS}/{id}
  • Method: GET
  • params: key, hostname, entity_id

This endpoint is called when inserting a saved block into a template. For synced sections, it is also called when loading a template that contains synced sections to ensure the latest content is displayed.

Response

STATUS 200

json
{
  "success": true,
  "data": {
    "id": 1,
    "name": "name of saved block or synced section",
    "type": "saved_section" or "synced_section",
    "created_at": "ISO 8601 time",
    "definition": "json of MJML section",
    "folder_id": 2
  }
}

Edit Saved Block, Synced Section or Folder

Request

  • URL: /{API.SAVED_SECTIONS}/{id}
  • Method: PATCH

This endpoint is called when editing the name, content, or folder location of a saved block, synced section, or folder.

json
{
  "entity_id": "entity user_id",
  "hostname": "origin",
  "key": "api key",
  "folder_id": 1,
  "definition": "json of MJML section",
  "name": "edited name"
}

Response

STATUS 200

Delete Saved Blocks, Synced Sections or Folders

  • URL: /{API.SAVED_SECTIONS}/delete
  • Method: POST

This endpoint is called when the user deletes saved blocks, synced sections, or folders. For synced sections, the last loaded version remains in templates where it was used.

Request

json
{
  "entity_id": "entity user_id",
  "hostname": "origin",
  "key": "api key",
  "blocksToDelete": [1, 2, 3]
}

Response

STATUS 200

Refresh Synced Sections

Call the refreshSyncedSections function on the TopolPlugin instance to update the synced sections listing and reload the template with the latest changes.

js
TopolPlugin.refreshSyncedSections();

This is useful when multiple users are working in the editor simultaneously - whether in the same template or different ones. If someone else creates, edits, or deletes a synced section, calling this function ensures your view stays up to date.

Preview Images

When creating a saved block or synced section, you can store a preview image on your server. The editor sends the block definition, and you can generate a preview image from it. Return the image URL in the image field when listing items.

Multi-language Support

If you're using multilingual templates, saved blocks and synced sections can include translations. The translation field contains language-specific content:

json
{
  "id": 1,
  "name": "Header Block",
  "type": "saved_section",
  "definition": "json of MJML section",
  "translation": {
    "en": { ... },
    "de": { ... }
  }
}