CMS blocks and widgets are how content moves from code to content team in Magento - but teams mix them up constantly, and the result is content that breaks on deploy, drifts between environments, or disables cache on the homepage. Used with a little discipline, they are a clean system. Here is the discipline.
Blocks vs Widgets: The Actual Difference
- CMS block: a reusable chunk of content - the content itself, managed in admin (Content > Blocks)
- Widget: a placement of a block (or dynamic component) into a page, container and theme location - the “where”, with optional parameters and scheduling
Simple rule: the block is the content, the widget is the delivery. Merchants editing a promotional message edit a block. Merchants deciding the message shows on the homepage hero for two weeks use a widget pointing at that block.
Admin vs Layout XML
Widgets can be created two ways, and the choice is an architecture decision:
- Admin-created widget instances: flexible, merchant-controlled, but they live in the database - invisible to git, different between staging and production, and a common source of “it worked on staging” content drift
- Layout XML widgets: declared in theme/module XML, version-controlled, deployed with code, identical everywhere
Our rule of thumb: structural placements in XML, campaign placements in admin. The homepage hero’s existence is code; which banner occupies it this week is content.
Environment-Safe Content
Blocks travel between environments badly (they are database rows). The mitigations:
- Keep structural content in code-deployed blocks where feasible (or seed blocks via data patches for content that must exist)
- For admin-managed blocks, adopt an export/import ritual per release - or accept and document that content is environment-specific
- Never hardcode store IDs or absolute URLs inside block content; use
{{store url=""}}directives so the same block works on staging and production
Caching: The Hidden Trap
A CMS block rendered through layout is cached with the page - fine for most content. The traps:
- Personalised content in blocks: a block showing customer-specific content must not sit in an FPC-cached page without hole-punching - use dynamic blocks (Adobe Commerce) or customer-data sections
- Widget TTL settings: some widget types have their own cache lifetime; a “today’s offer” widget with a 24-hour TTL shows yesterday’s offer
- Cache invalidation after edits: block edits invalidate block_html cache - make sure your deployment and cache-flush habits account for it, or editors report “my change didn’t go live” when it did, to a stale cache
Done right, the split is clean: developers own structure and deployment, merchants own message and timing, and nobody debugs content drift at 6pm on a Friday. That calm is worth the small amount of upfront doctrine.