Twig Helpers & Filters¶
The Twig engine exposes the MageObsidian phtml bridge as Twig functions, and Magento's context-aware escapers as filters. They mirror the methods you already use in .phtml.
The rendering block is read from the Twig context automatically, so nested and recursive renders each address their own block β you never pass block to these helpers.
Functions¶
| Function | Maps to | Returns |
|---|---|---|
render_vue(name, props = {}, eager = false) |
renderVueComponent() |
A Vue island marker (safe HTML). |
__(text, ...args) |
Magento's __() |
Translated text with %1/%2 argument substitution (auto-escaped). |
child_html(alias = '', use_cache = true) |
getChildHtml() |
A child block's HTML (safe). |
hero_icon(name, set = 'solid', size = '24') |
getHeroIcon() |
An inline Heroicons SVG (safe). |
vite_url(path) |
getViteFileUrl() |
URL of a Vite-generated file. |
component_path(name) |
resolveComponentPath() |
Resolved URL of a component by its Vendor::Component name. |
view_file_url(file_id, params = {}) |
getViewFileUrl() |
URL of a view file. |
json_ld(type, data = {}) |
renderJsonLd() |
A schema.org JSON-LD <script> for a custom type (safe HTML). |
image(src, options = {}) |
renderImage() |
A CWV-friendly <img>/<picture> (safe HTML). |
The markup-emitting helpers (render_vue, child_html, hero_icon, json_ld, image) are flagged safe, so Twig's auto-escaping leaves their HTML intact. The URL helpers return plain strings and are auto-escaped like any value.
Note:
render_vue,hero_icon,vite_url,component_path,json_ldandimagerequire the rendering block to extendMageObsidian\ModernFrontend\Block\Template. If a.twigis rendered by an unrelated block, the helper raises an actionable error naming the missing method.child_htmlandview_file_urlwork on every Magento block;__is block-independent.
Formatting helpers¶
A second extension (FormatExtension) adds framework-level formatting backed by Magento services (not by the rendering block, so these work in any .twig). All output is plain text and is auto-escaped β none are flagged safe. Each tolerates null/empty input.
| Helper | Backed by | Use |
|---|---|---|
value\|number |
LocaleFormatter |
Group a number per the store locale (1234567 β 1,234,567). |
date\|date_format(format = 'medium', part = 'date') |
TimezoneInterface |
Locale/timezone date. format: short\|medium\|long\|full; part: date\|time\|datetime. |
html\|strip_tags(allowed = null) |
Filter\StripTags |
Strip markup to plain text. |
config(path, scope = 'store') |
ScopeConfigInterface |
A store-config value. |
url(route, params = {}) |
UrlInterface |
A framework URL for a route. |
media_url(path) |
store media base | A URL under the store's media base. |
Commerce formatting lives in the storefront module's PriceExtension (registered on the engine via DI β see Extending the engine): amount\|price(include_container = true) and amount\|currency(code = null), both is_safe html because PriceCurrency::format() returns a <span class="price">.
Examples¶
render_vuemounts the component with the defaultvisible(lazy) strategy. For an above-the-fold island (header, mini-cart trigger), passtrueas the third argument to mount iteagerly β mirroring$block->renderVueComponent($name, $props, true)in.phtml.
Filters¶
HTML escaping is already Twig's default, so there is no escape_html filter β just output a value. The remaining context-aware escapers mirror Magento's $escaper->escape* for the cases where HTML escaping is the wrong context:
| Filter | Maps to | Use for |
|---|---|---|
escape_url |
escapeUrl() |
A value placed in an href/src. |
escape_html_attr |
escapeHtmlAttr() |
A value placed in an HTML attribute. |
escape_js |
escapeJs() |
A value injected into a JS context. |
escape_css |
escapeCss() |
A value injected into a CSS context. |
Examples¶
Composition: includes, embeds & macros¶
All template references use the Vendor_Module::path.twig notation, resolved through Magento's theme fallback β so a child theme overrides any partial/macro file exactly like a .phtml. Prefer composing templates over copy-pasting markup.
{% extends %} + {% block %} β a base layout with overridable regions:
{% include %} with with/only β a parameterized partial (the only keeps the partial's scope isolated):
{% embed %} β the Twig equivalent of slots. Include a shell and fill its blocks at the call site. Ideal for cards/sections whose chrome is shared but whose contents vary:
{% macro %} β reusable fragments without a layout round-trip. Import once, call many times:
ViewModels in Twig¶
Keep data logic out of templates: put it in a ViewModel (ArgumentInterface), register it as a block <argument> in layout, and read it from Twig via the block's magic getter.
The same ViewModel can be registered on several blocks (header, footer, β¦) so they share one source of truth β no duplicated data in templates.
Extending the Twig engine¶
The shared Twig environment is built by EnvironmentFactory, whose extensions array argument merges across modules by item key β like TemplateEngineFactory's engines. Any module adds its own filters/functions by registering a Twig\Extension\ExtensionInterface on that array, without editing the engine:
This is exactly how the storefront's PriceExtension adds price/currency.
Declare it at global scope, not
etc/frontend/di.xml. Array arguments merge cumulatively within a scope, but an area scope replaces a global array instead of merging β which would drop the engine's own extensions in the frontend. The extension is only instantiated when the (frontend) environment is built, so a global declaration is inert elsewhere. Filter/function names are a shared namespace; prefix third-party names to avoid collisions (last one wins).
Next Steps¶
- Twig Engine β installation and how Twig coexists with phtml.
- Vue Islands β what
render_vueemits and how it hydrates.