Building a Custom Block in Sulu CMS: From Zone Configuration to Frontend Rendering
Learn how to add a reusable callout block to Sulu CMS by creating a PHP block class, a Twig template, and registering it with Sulu—so editors can drop it into any zone without touching layout files.
19 Jul 2026, 02:58 UTC

Problem: Adding a Reusable Callout Without Touching Templates
You need a styled callout box that editors can drop into any page zone, but you don’t want to edit Twig layouts each time a new callout is required. The goal is to let administrators add the component through the Sulu admin UI while keeping the frontend rendering consistent.
Takeaway
By defining a custom block class and a Twig template, you can make the callout available as a draggable block in any zone. The three‑tier hierarchy (Page → Zone → Block) handles storage, rendering, and inheritance automatically.
Understanding the Page‑Zone‑Block Hierarchy
Sulu stores content in three related tables:
sulu_page– represents each node in the page tree.sulu_zone– belongs to a page and defines a named layout area (e.g., "main", "sidebar").sulu_block– belongs to a zone and holds the serialized data of a specific block type.
When a page is rendered, Sulu loads its zones, then instantiates each block’s PHP class and passes the block’s data to the corresponding Twig template.
Creating a Custom Callout Block
- Create the PHP block class
Place the class in
src/Block/CalloutBlock.php. It must extendSulu\Bundle\BlockBundle\Block\BlockInterfaceor use the provided abstract base.<?php namespace App\Block; use Sulu\Bundle\BlockBundle\Block\BlockInterface; use Sulu\Bundle\BlockBundle\Block\BaseBlock; class CalloutBlock extends BaseBlock implements BlockInterface { public function getName(): string { return 'callout'; } public function getTemplate(): string { return '@App/block/callout.html.twig'; } public function getViews(): array { return [ 'default' => [ 'title' => 'Callout', 'icon' => 'comment', 'properties' => [ ['name' => 'title', 'type' => 'text'], ['name' => 'content', 'type' => 'html'], ], ], ]; } }Required permissions: the web‑server user must be able to read the
src/directory. No database changes are made yet. - Add the Twig template
Create
templates/block/callout.html.twig:{# templates/block/callout.html.twig #}{{ block.title|default('Callout') }}
{{ block.content|raw }}Make sure the template directory is registered in your Symfony
framework.templating.pathsif you are not using the defaulttemplates/location. - Register the block with Sulu
Add a
sulu_block.xmlfile inconfig/packages/(or extend an existing one):<?xml version="1.0" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sulu="http://sulu.io/schema/dic/sulu" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://sulu.io/schema/dic/sulu http://sulu.io/schema/dic/sulu/sulu-1.0.xsd"> <sulu:block> <sulu:block-type>App\Block\CalloutBlock</sulu:block-type> </sulu:block> </container>After adding the file, clear the cache:
# Run as the application user (e.g., www-data) php bin/console cache:clear --env=prodRisk: clearing the prod cache will briefly serve stale responses; schedule during low‑traffic periods or use Symfony’s warm‑up command.
- Make the block available in a zone
In the Sulu admin, edit a page, click the “Zones” tab, choose a zone (e.g., “main”), and drag the newly appeared “Callout” block into the zone. Fill the title and content fields, then save.
Trade‑offs and Limitations
- Performance: Deeply nested zones (zones inside zones inside zones) increase the number of Doctrine queries and Twig renders. Keep the hierarchy shallow (
Page → Zone → Block) for predictable latency. - UI Consistency: Because administrators can place any block in any zone, a design system or governance policy is needed to prevent layout clashes.
- Version Assumptions: The example assumes Sulu 2.x (Symfony 5+). The block interface and XML configuration have remained stable since Sulu 2.0.
Verification
- Open the Sulu admin, navigate to a page, and confirm the “Callout” block appears in the zone’s block picker.
- Save the page and view the frontend; inspect the rendered HTML for the
<div class="callout">wrapper. - Optionally, check the database:
SELECT * FROM sulu_block WHERE block_type = 'App\\Block\\CalloutBlock';should return a row with the saved title and content serialized in thedatacolumn.
Closing
Defining a custom block in Sulu gives editors a reusable, drag‑and‑drop component while keeping the rendering logic in a single Twig template. By respecting the Page‑Zone‑Block hierarchy and watching for nesting depth, you maintain both flexibility and predictable performance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.