Sections
Sections are reusable editable content fragments. They are useful for content that appears in several pages or templates, such as a shared banner, a newsletter call to action, a support strip, or a reusable product block.
A section is managed outside a page, has its own versions, and can be rendered from another CMS page, from a module, or directly from Twig.
When to use sections
Use a section when the same editable content must be maintained once and displayed in more than one place.
Use a normal page module when the content belongs only to that page. Use a block when you need the block system behavior and its block-specific configuration. A section is closer to a small reusable CMS page: editors can build it with modules, preview it, publish a version, and then reference it elsewhere.
Common examples:
- site-wide calls to action
- reusable header or footer fragments
- campaign strips reused across landing pages
- editorial components shared by several content types
- fragments rendered asynchronously or through ESI
Install the plugin
Sections are provided by softspring/cms-sections-plugin.
Install the package with Composer:
composer require softspring/cms-sections-plugin
If your project does not use Symfony Flex, enable the plugin in config/bundles.php:
return [
// ...
Softspring\CmsSectionsPlugin\SfsCmsSectionsPlugin::class => ['all' => true],
];
The plugin prepends its CMS collection automatically:
sfs_cms:
collections:
- vendor/softspring/cms-sections-plugin/cms
That collection provides the section module and its form, edit, and render templates.
Register routes
Add the frontend route used to render sections:
# config/routes/sfs_cms.yaml
_sfs_cms_sections_frontend_:
resource: "@SfsCmsSectionsPlugin/config/routing/frontend_sections.yaml"
prefix: /{_locale}
The plugin exposes the section render route at:
/{_locale}/__/s/{section}
Add the admin routes where you want the CMS backoffice to manage sections:
# config/routes/admin/sfs_cms.yaml
_sfs_cms_sections_:
resource: "@SfsCmsSectionsPlugin/config/routing/admin_sections.yaml"
prefix: "/cms/sections"
These routes add the section list, create, update, delete, preview, content edition, version list, publish, unpublish, recompile, clear compiled content, and cleanup actions.
Register the admin routing provider
The admin route file includes an internal plugin route type named sfs_cms_plugin_admin_section. If your installed plugin version does not register a routing provider for this type, Symfony fails while loading routes with this error:
Cannot load resource ".". Make sure there is a loader supporting the "sfs_cms_plugin_admin_section" type.
In that case, register a small routing provider in the application:
<?php
declare(strict_types=1);
namespace App\Cms\Infrastructure\Routing;
use Softspring\CmsBundle\Routing\Provider\RoutingProviderInterface;
use Symfony\Component\Routing\RouteCollection;
final class SectionsAdminRoutingProvider implements RoutingProviderInterface
{
public function supportedTypes(): array
{
return ['sfs_cms_plugin_admin_section'];
}
public function supports(string $type): bool
{
return in_array($type, $this->supportedTypes(), true);
}
public function getAdminRoutes(string $type): RouteCollection
{
return new RouteCollection();
}
}
Register it with the CMS routing provider tag:
# config/services.yaml
services:
App\Cms\Infrastructure\Routing\SectionsAdminRoutingProvider:
tags: [ 'sfs_cms.routing_provider' ]
This provider only makes Symfony accept the plugin route type. It does not add routes, because the section routes are already declared explicitly by the plugin route file.
Add security roles
Import the role hierarchy shipped by the plugin:
# config/packages/security.yaml
imports:
- { resource: '@SfsCmsSectionsPlugin/config/security/admin_role_hierarchy.yaml' }
Then give your admin role one of the section roles:
security:
role_hierarchy:
ROLE_ADMIN:
- ROLE_SFS_CMS_ADMIN_SECTIONS_RW
Available roles:
ROLE_SFS_CMS_ADMIN_SECTIONS_ROcan list, read, preview, and inspect versions.ROLE_SFS_CMS_ADMIN_SECTIONS_CREATORcan also create sections and versions.ROLE_SFS_CMS_ADMIN_SECTIONS_PUBLISHERcan update, publish, unpublish, delete, keep versions, recompile, clear compiled data, and clean old versions.ROLE_SFS_CMS_ADMIN_SECTIONS_RWincludes the publisher role.
Add the admin menu entry
If your project builds the admin menu manually, add an entry that points to sfs_cms_admin_sections_list:
twig:
globals:
admin_menu:
cms:
sections:
translation_key: 'sidebar.menu.cms.sections'
route: 'sfs_cms_admin_sections_list'
role: PERMISSION_SFS_CMS_ADMIN_SECTION_LIST
active_expression: 'sfs_cms_admin_sections_'
icon: 'bi bi-grid-fill me-2'
Add admin assets
The plugin includes JavaScript for section previews and for the section form type messages. Add the asset package and import its admin entry when your admin build does not already include it:
yarn add "file:vendor/softspring/cms-sections-plugin/assets" --dev
// assets/admin.js
import '@softspring/cms-sections-plugin/scripts/admin-cms';
Configuration reference
The plugin works with defaults, but projects can tune the section entity classes, compiled output, automatic compilation, and recompile behavior.
For the option reference, see Configure CMS Sections.
Create a section
In the admin area, open CMS / Sections and create a new section.
The section form asks for:
name, used by editors to identify the section.ttl, stored as section extra data and used for HTTP cache max age when the section is rendered through the section endpoint.defaultLocale, used as the base locale for editable fields.locales, the languages enabled for this section.notes, shown to editors when the section is selected from a page module or form field.
After creating the section, edit its content. A section version uses the same module collection editor as CMS pages, so editors can compose the section with the configured CMS modules.
Saving creates a version. Publishing marks that version as the public version of the section.
Insert a section in CMS content
The plugin provides a section module. It can be added to a page or content type like any other CMS module.
The module fields are:
id, optional HTML id wrapper.class, optional CSS classes wrapper.section, the section to render.mode, the render mode.
Render modes:
embeddedrenders the section through Symfony sub-request rendering.esirenders an<esi:include>tag that points to the section endpoint.ajaxrenders an empty placeholder and loads the section endpoint in the browser.
Choose embedded when the section is part of the page response and should be available immediately. Choose esi when your HTTP cache or reverse proxy is configured to process ESI. Choose ajax when the fragment can load after the page and you want to keep it independent from the page response.
The admin form warns editors when a draft section is selected or when a published section uses ESI/AJAX with or without a TTL.
Render sections from Twig
You can render sections directly from Twig.
Render by id:
{{ sfs_cms_section('018f2f6a-8d89-7a0c-9a4c-8e4b9c2f5d31') }}
Find and render:
{% set section = sfs_cms_section_find({ name: 'Footer CTA' }) %}
{{ sfs_cms_section(section) }}
Render with ESI:
{{ sfs_cms_section_esi(section) }}
Render with AJAX:
{{ sfs_cms_section_ajax(section) }}
You can also use dynamic field helpers:
{{ sfs_cms_section_embed_by_name('Footer CTA') }}
{{ sfs_cms_section_esi_by_name('Footer CTA') }}
{{ sfs_cms_section_ajax_by_name('Footer CTA') }}
The find_by_* and render *_by_* helpers add the dynamic field to the criteria. For example, sfs_cms_section_embed_by_name('Footer CTA') searches with name = Footer CTA.
Render behavior and cache
The frontend render controller loads the section by id and renders its published version. If the section has no published version, the route returns a not found response unless do_not_throw_not_found is passed.
Admin preview is different: when a section has no published version, preview can use the last version so editors can inspect drafts.
When the response is successful and the section has a ttl, the controller marks the response as public and sets max-age to that value. This matters most for ESI and AJAX rendering, because both call the section endpoint separately.
When compiled content contains render errors, the section endpoint returns HTTP 500. This makes broken reusable fragments visible during publishing or direct rendering instead of silently serving invalid content.
Versions and publishing
Each section keeps its own versions. A version contains the module data, linked medias, linked routes, linked sections, compiled data, and version metadata.
The admin area supports:
- creating a version from the last version or from a selected previous version
- previewing a version
- publishing a version
- keeping or unkeeping a version so cleanup does not remove it
- editing the version note
- recompiling a version
- clearing compiled data
- deleting a version
- cleaning versions that are eligible for cleanup
- unpublishing the section
When autocompile_on_publish is enabled, publishing compiles the section for every configured site and every locale enabled on the section. The published version is also marked as kept.
Linked usages
The CMS tracks section relationships in content versions and compiled data. This lets the admin area show where a section is used.
The relationship is created when a section is selected by the section module or by a field using the section form type. This is useful before editing or unpublishing a shared section, because editors can inspect which pages or other sections depend on it.
Use the section form type in custom modules
The plugin registers a CMS form field type named section. Use it in module configuration when a custom module needs to reference a reusable section:
module:
revision: 1
group: 'cms-blocks'
edit_template: '@module/my_module/edit.html.twig'
form_template: '@module/my_module/form.html.twig'
render_template: '@module/my_module/render.html.twig'
module_options:
form_fields:
section:
type: 'section'
The field lists sections by name. Draft sections are marked in the choice label, and the current section is filtered out when editing a section so editors cannot create a direct self-reference.
The form type adds data attributes used by the admin JavaScript:
- section detail URL
- draft state
- TTL value
- section notes
- preview URLs for each configured site and enabled locale
Extend the section model
Projects can use custom Doctrine entities for sections and section versions. Configure them with sfs_cms_sections.section.class and sfs_cms_sections.section.version_class.
The section entity should extend the base model or implement SectionInterface. The version entity should extend the base version model or implement SectionVersionInterface.
Use this when the project needs extra metadata, project-specific relations, or custom admin behavior around reusable fragments. Keep the standard versioning and compilation behavior unless there is a clear reason to change it, because the render controller, Twig helpers, admin actions, and relationship listeners expect the section and version contracts.