monofony/ui-bundle
Monofony UI Bundle provides reusable Twig templates, layout components, and styling helpers for Monofony and Symfony applications, helping teams build consistent storefront and admin interfaces faster with less custom frontend code.
monofony/ui-bundle is the UI layer lifted out of Sylius: Twig layouts, macros (buttons, messages, pagination, headers, breadcrumbs), a form theme, and the Semantic UI-flavoured assets that go with them. It ships no business logic — it is a template/asset library wrapped in a bundle. Treat it as a design system you import into your own templates, not a component you configure heavily.
1. Install
composer require monofony/ui-bundle
The current release is v1.10.0, which adds PHP 8 support and drops PHP 7.3. So the floor is PHP 7.4 and the ceiling is "whatever ^8.0 resolves to" — it does not mean the templates have been tested on 8.3/8.4. Check what it drags in before you commit:
composer why monofony/ui-bundle
composer show monofony/ui-bundle --tree
If you are still on PHP 7.3 you cannot take v1.10.0 at all and are stuck on the previous tag — plan the PHP bump first, or fork. Either way, the Twig/Symfony constraints are still 2/3 and 4/5 era, so the PHP 8 support is the interpreter, not the framework stack.
2. Register the bundle
Look in vendor/monofony/ui-bundle/src/ for the single *Bundle.php class (this is a read-only subtree split, so the class name and namespace are whatever the fork kept — verify before copy-pasting docs from Sylius):
// config/bundles.php
return [
// ...
Monofony\Bundle\UiBundle\MonofonyUiBundle::class => ['all' => true],
];
3. Find the template namespace
The Twig prefix is derived from the bundle name, so it is either @SyliusUi/ (upstream naming) or @MonofonyUi/. Confirm in one command instead of guessing:
bin/console debug:twig --filter=Macro
4. Look at the source first
vendor/monofony/ui-bundle/src/Resources/views/ is the entire public API. Read the macro signatures before writing markup — every macro in Macro/*.html.twig starts with a {% macro name(args) %} you can copy from. Assets live under src/Resources/public (or assets/); install/symlink them:
bin/console assets:install --symlink
5. First use case
Don't start with a layout. Start with the flash-message macro in one existing template — it is the highest-value, lowest-risk piece:
{# templates/base.html.twig #}
{% import '@SyliusUi/Macro/messages.html.twig' as messages %}
<div class="ui container">
{{ messages.flash() }}
{% block body %}{% endblock %}
</div>
Once messages.flash() renders your addFlash('success', ...) calls correctly, the bundle is wired up and you can move on to layouts and the form theme.
Import macros at the top of the file; call them anywhere. The common set:
{% import '@SyliusUi/Macro/buttons.html.twig' as buttons %}
{% import '@SyliusUi/Macro/actions.html.twig' as actions %}
{% import '@SyliusUi/Macro/pagination.html.twig' as pagination %}
{% import '@SyliusUi/Macro/headers.html.twig' as headers %}
{% import '@SyliusUi/Macro/breadcrumbs.html.twig' as breadcrumbs %}
Typical index page composition:
{{ headers.index('app.product.title'|trans) }}
{{ breadcrumbs.show({'app.dashboard': path('app_dashboard')}, 'app.product.title'|trans) }}
{{ actions.create(path('app_product_create'), 'app.product.create'|trans) }}
{# ...table... #}
{{ pagination.simple(products) }}
Each macro takes an optional options hash as the last argument ({ 'class': 'primary', 'icon': 'plus' }) — that is how you customise without forking the template.
Symfony's bundle override convention is the extension point:
templates/bundles/SyliusUiBundle/Macro/buttons.html.twig
Copy the vendor file in, change it, done. This survives composer update. If you only need one macro variant, prefer wrapping it in your own templates/_macros/buttons.html.twig that imports the vendor one and adds your variants — far less drift to maintain.
The layout templates define named blocks (content, header, sidebar, flash, javascripts). Extend and fill:
{% extends '@SyliusUi/Layout/base.html.twig' %}
{% block header %}
{{ parent() }}
<div class="ui segment">{{ block('title') }}</div>
{% endblock %}
Because parent() works normally here, this is a clean way to inject analytics, a nav bar, or a user menu without touching the vendor layout.
Register it globally and you get Semantic UI markup on every form for free:
# config/packages/twig.yaml
twig:
form_themes:
- '@SyliusUi/Form/theme.html.twig'
Ordering is significant — the last registered theme wins for conflicting blocks. If you have your own theme, put it after the vendor one:
form_themes:
- '@SyliusUi/Form/theme.html.twig'
- 'form/theme.html.twig'
For one-off forms, apply per-form instead of globally:
{% form_theme form '@SyliusUi/Form/theme.html.twig' %}
The bundle has no routes of its own; your controllers render your templates, which import vendor macros. Keep the controller thin and push all presentation decisions into the template layer — that is where this package's value is.
public function index(ProductRepository $repo): Response
{
return $this->render('product/index.html.twig', [
'products' => $repo->createPaginator(),
]);
}
headers.*, buttons.*, and actions.* macros translate keys you pass in. They do not ship your domain translations. Keep your keys in translations/messages.en.yaml and be consistent — the macros pass 'app.product.create' straight to the translator, so a typo silently renders the raw key rather than failing.
It is a template library with a bundle wrapper — do not expect configuration. There is essentially no Configuration.php surface worth tuning. Time spent looking for a config tree is wasted; time spent reading Resources/views/ is not.
v1.10.0 changes your PHP constraint, nothing else. The release notes are two lines: PHP 8 support added, PHP 7.3 dropped. No Twig or Symfony constraint bumps, no template changes, no new macros. So: this is a green light for modern PHP runtimes, not a signal that the package is being actively developed again. Before upgrading, confirm the resolved framework versions still fit your app:
composer show monofony/ui-bundle --tree | grep -E 'twig|symfony'
If composer update now pulls a Twig or Symfony major you did not intend, that came from your own constraint resolution, not from this release — pin accordingly.
Maintenance risk remains the headline, just slightly less acute. Previous release was April 2021; v1.10.0 is a maintenance bump, not a revival. It will not track Twig 4 or Symfony 7. Pin it, and budget for the day you vendor the macros into your own templates/_components/ directory — that migration is easy if you never let vendor overrides sprawl.
Namespace collision with sylius/ui-bundle. This is a fork of the same codebase and almost certainly declares replace/shares the Sylius\Bundle\UiBundle namespace. Never install both. Check composer show | grep -i ui-bundle and look at the replace block in vendor/monofony/ui-bundle/composer.json before adding anything Sylius-adjacent.
Template cache will hide your override. If an override "doesn't apply", it is nearly always the bundle-name path being wrong, not the cache. Symfony resolves overrides by the bundle's short name:
bin/console debug:twig --filter=Buttons # shows both vendor path and any override path
templates/bundles/SyliusUiBundle/... vs templates/bundles/MonofonyUiBundle/... — one of them is silently ignored.
Asset pipeline is on you. The bundle ships Semantic UI-flavoured CSS/JS. With Webpack Encore you must add the vendor path to your entrypoint imports; with AssetMapper you must map vendor/monofony/ui-bundle/src/Resources/public. Nothing auto-registers the stylesheet, so unstyled markup after install means "go configure assets", not "the bundle is broken".
Macro imports must be at the top level of the template. Twig does not allow {% import %} inside a {% block %} in older versions shipped with this bundle. If you get "Unexpected tag name 'import'", hoist it above the first block.
include vs import. Macros (Macro/*.html.twig) must be imported and called. Partials (files starting with _) must be included and receive a context hash. Mixing the two produces "Macro X is not defined" or silently-empty output.
Block name collisions. Extending the vendor layout while also extending a template that already defines content will bite you. Print the block tree when confused:
bin/console debug:twig --filter=Layout
Debug output of a macro's options. Every macro ends with an attr/options merge. Dump the defaults by temporarily {{ dump(buttons.create(null, null, {})) }} in the dev environment — faster than reading 200 lines of Twig.
Extension points, ranked by safety:
templates/bundles/<BundleName>/... override of a single file.packages/ (only when you need to change PHP-side Twig extensions, which you almost never do).Add twig:lint to CI before you upgrade anything. Now that v1.10.0 lets you run on PHP 8, the risk has moved: your PHP bump is the more likely breakage than the Twig bump, and a composer update can move both at once. Lint catches neither at compile time for all cases, but it is the cheapest guard:
bin/console lint:twig templates
bin/console lint:container
Search cost is low, so use it. Because there are no dependents and near-zero documentation drift, grep -r "macro " vendor/monofony/ui-bundle/src/Resources/views/ is a faster and more accurate API reference than any blog post written about Sylius UI in 2019 — and it is the only thing v1.10.0 did not change.
How can I help you explore Laravel packages today?