The ultimate Sylius UX Components guide
In our latest Sylius project, we make heavy use of Sylius UX Components, e.g. Twig Components and Live Components.
Combining both with Sylius Twig Hooks, you can build a fully customizable, extendable, and interactive UI interfaces without the need for writing much Javascript code.
Twig Components
Twig components give you the power to bind an object to a template. This allows you to make use of custom logic to generate the data needed by your template, e.g. querying data from a repository or doing some form of complex calculations.
How does it work? Place a new class in the App\Twig\Components
namespace. Let's create a class called SimpleComponent
:
<?php
declare(strict_types=1);
namespace App\Twig\Components;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
class SimpleComponent
{
public ?string $name;
}
To let Symfony know that this class is a Twig component, the #[AsTwigComponent]
attribute is added to the class.
To define a custom name for the component, we can tag the service with the sylius.twig_component
tag and configure the identifier sylius_shop:my:simple_component
for further use:
App\Twig\Components\SimpleComponent:
class: App\Twig\Components\SimpleComponent
tags:
- { name: sylius.twig_component, key: sylius_shop:my:simple_component }
This allows us now to use the component in the Sylius Twig Hook configuration by referring to the sylius_shop:my:simple_component
:
'sylius_shop.homepage.index':
my_component:
component: 'sylius_shop:my:simple_component'
props:
name: 'This is my single component'
template: 'components/my_simple_component/component.html.twig'
When configuring a Live Component in the Sylius Twig Hooks, configuration is passed by the props
key. At least, we need to pass the template path.
Any public property of the component class can also be set via the props
key, e.g. we set the name
property to the value This is my single component
in the example above.
In our Twig template, the name
property is automatically accessible as other variables, e.g. as:
{{ name }}
Live Components
Live Components extend the Twig Components by adding the ability to change the component's state by calling code on the server side and automatically updating the HTML in your browser. For this you don't need to write a single line of Javascript code.
Similar to the Twig Components, by default Live Components should be placed in the App\Twig\Components
namespace. Let's create a class called LiveComponent
in this namespace:
<?php
declare(strict_types=1);
namespace App\Twig\Components;
use Sylius\Bundle\UiBundle\Twig\Component\TemplatePropTrait;
use Sylius\TwigHooks\LiveComponent\HookableLiveComponentTrait;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
class LiveComponent
{
use DefaultActionTrait;
use HookableLiveComponentTrait;
use TemplatePropTrait;
#[LiveProp]
public string $name = '';
}
By convention a Live Component needs an _invoke()
method. This method is called when the component is rendered in the browser. You can either add this method yourself or use the DefaultActionTrait
which provides this method for you.
Also, since we are in Sylius (and more specifically in Sylius Twig Hook) Context, and we need to make sure that the component also defines a $hookableMetadata
property. The simpler way to do this is to use the HookableLiveComponentTrait
provides by Sylius.
The TemplatePropTrait
provides the $template
property which holds the template path for the component that we can be defined in the twig Hooks configuration.
To let Symfony know that this class is a Live component, the #[AsLiveComponent]
attribute has to be added to the class.
To define a custom name for the component, we can tag the service with the sylius.live_component.shop
tag and configure the identifier sylius_shop:my:live_component
for further use:
App\Twig\Components\LiveComponent:
class: App\Twig\Components\LiveComponent
tags:
- { name: sylius.live_component.shop, key: sylius_shop:my:live_component }
In the Sylius Twig Hook configuration we can now use the sylius_shop:my:live_component
identifier to refer to the component:
'sylius_shop.homepage.index':
live_component:
component: 'sylius_shop:my:live_component'
props:
name: 'This is my name'
template: 'components/my_live_component/component.html.twig'
Next, we have to prepare the Twig template for the component. By definition a Live Component template, needs one top-level HTML node and we need to pass the attributes
data to it. This helps the default Live Component Javascript logic to update the component's state when things change in the frontend:
<div {{ attributes }}>
{{ name }}
</div>
Similar to the Twig Component configuration, the props
key is used to pass data to the component. At least, we need to supply the path to the Twig template.
Rendering resource forms with Live Components
If you need to render a resource form in a Live Component, e.g. because you need an interactive form in your UI, Sylius provides the ResourceFormComponentTrait
trait that you can use in your Live component:
<?php
declare(strict_types=1);
namespace App\Twig\Components;
use Sylius\Bundle\UiBundle\Twig\Component\ResourceFormComponentTrait;
use Sylius\Bundle\UiBundle\Twig\Component\TemplatePropTrait;
use Sylius\TwigHooks\LiveComponent\HookableLiveComponentTrait;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
class LiveComponent
{
use DefaultActionTrait;
use HookableLiveComponentTrait;
use TemplatePropTrait;
use ResourceFormComponentTrait;
public function __construct(
RepositoryInterface $repository,
FormFactoryInterface $formFactory,
string $resourceClass,
string $formClass,
)
{
$this->initialize(
$repository,
$formFactory,
$resourceClass,
$formClass
);
}
}
The call to $this->initialize()
is required to make the component aware of the resource, the form class, and the repository used for storing the data.
In the Sylius Twig Hook configuration, you now have to pass the resource
and form
props to the component:
'sylius_shop.homepage.index':
live_component:
component: 'sylius_shop:my:live_component'
props:
name: 'This is my name'
template: 'components/my_live_component/component.html.twig'
resource: '@=_context.resource'
form: '@=_context.form'
The main Sylius logic, populates the data in the context
variable which is passed to the component in the process of rendering the Twig Hooks and can be used to access the information in your component configuration.
In your Twig template, you render the form as you would normally do in Symfony.
When a form field is changed, the form data is now sent to the server, evaluated, and sent back to the client. This allows you to update the component's state and validate the form data in real time. Or, you can add custom functionality that generates data based on one form field and populates another form field with the result. The possibilities are endless.
Conclusion
Sylius UX Components are a great way to build customizable, extendable, and interactive UI interfaces without the need for writing much Javascript code. They are very well included in the Sylius Twig Hooks system which makes it very easy to work with in context of a Sylius application.
Additionally, the UX Component foundation in Sylius helps to extend core Sylius components without the need to completely overwrite them with custom code. I've covered this earlier this year how to extend Twig Components and Live Components by hooking into the rendering events of the Twig components.