Miscommunication in our projects is costly. A single misunderstood User Story can result in 3 days of wasted development time. Additionally, when developers do not use the same programming language, it may be necessary to construct APIs to facilitate communication, which can also be expensive. It is important to consider why front-end developers may be hesitant to work with Twig and how this can lead to a disconnect between front-end and back-end development.
Front-end developers, like back-end developers, have their own set of habits, principles, and rules that make them feel safe, efficient, and at home.
One of the most important principles is Component Architecture, which serves as the foundation for major JavaScript frameworks such as Svelte, React, and Vue.
What is Component Architecture?
4 main rules define Component Architecture.
Composition
A page is no longer just a page, but rather a collection of small, reusable components. These components can be assembled to form a page. For example, there could be a component for the title and another for the training list. The training list component could even be composed of smaller components, such as a training card component. The goal is to create the most atomic components possible.
Independence
For a component to be infinitely reusable, it should not be aware of its context. It should function identically when placed on another page.
Properties
Our component must remain independent, but we can customize it based on the context. For instance, we can change the label of a button while keeping its appearance the same by assigning it properties. Properties are attributes that the component will adopt at creation and maintain throughout its life.
States
A component can have multiple states. For instance, a button can transition from an active state to a loading state and then to a disabled state. Typically, a state principle is used to achieve this. The difference between a state and a property is that a property remains constant throughout the lifespan of the component, while a state changes.
Does it work with Symfony?
The answer is yes!
Start by installing
composer require ux-twig-component
Then in your template folder, create a component folder. Inside the component folder, add a file named Button.html.twig.
<button class="btn btn-primary">Click me</button>
And just like that… you have created your first component! Congrats
Now, how will you use it? Let's assume you want to use it on your homepage. Open your home.html.twig template.
// the content of your template
<twig:Button/>
This way, you have successfully imported a component for the first time.
Can I include properties for this component?
Sure, you can! Simply go back to your button template and add a label property like this:
{% props label = 'Click me' %}
<button class="btn btn-primary">{{ label }}</button>
Now, our component has a label property, with a default value 'Click me'. Without this property, using the component would result in an error.
To assign a property to a component, follow these steps:
<twig:Button label="Submit"/>
How do we do composition?
If we want our component to contain an icon before our label, let's modify the template of our button.
{% props label = 'Click me' %}
<button class="btn btn-primary">
{% block content %}
{% endblock %}
{{ label }}
</button>
So, I have added a block that I have named content. The term "content" is important because it corresponds to the default block.
You can now use your button in this way:
<twig:Button label="Submit">
<twig:Icon/>
</twig:Button>
We added an Icon component to the Button component by placing it between the tags. This transfers the Icon component to the content block.
This block is a powerful tool for for precise composition. You can even create your own blocks.
{% props label = 'Click me' %}
<button class="btn btn-primary">
{% block content %}
{% endblock %}
{{ label }}
{% block after_label %}
{% endblock %}
</button>
<twig:Button label="Submit">
<twig:Icon/>
<twig:block name="after_label">
+
</twig:block>
</twig:Button>
Note that we use the <twig:block tag to easily refer to the block of our component.
And what about the states of our component?
You have two options to manage the states of your component:
- For the first option, if you want your component's CSS classes to change based on user actions, you can use Stimulus, a small JavaScript library that integrates perfectly with Symfony through the StimulusBundle. With just a few lines of JavaScript code, you can achieve your goal.
- But imagine having a component with complex states that depend on the back-end. In this case, you have the option of using LiveComponents. I won't go into details, but here is what you need to remember: with very little code, you can perform complex actions on your back-end without reloading the page.
The present article is meant to give you an overview of how to use Component Architecture with Symfony. I know this article will be read mainly by back-end developers, but spread the word. It is suitable for both back-end and front-end developers. Even front-end developers can feel at home with Symfony. And you, back-end developers, you can continue to have fun while developing a quality front-end.
Thank you for reading!
Top comments (3)
Thank you for the wonderful contributions.
I am still skeptical about twig components and live components.
By including and embedding you can also parameterize your templates very well. And javascript stuff goes a long way with stimulus and turbo.
I would like to leave it at that and save myself any further abstraction and the associated overhead.
But I still find it very interesting to see what's going on in the UX area with Symfony.
My experience with front-end developers not liking twig is because the dynamic data is wrapped in an object they have to debug to get the data. I'm looking at Drupal as the worst offender.
You can create components by using
include
templates. By using a namespace you can identify those templates as components better,$loader->addPath('./templates/components', 'components');
.But the way this package adds their templates made me look into how they did it.
<twig:Alert message="hello!" user="{{ user.id }}" />
look more htmly than{{ component('Alert', {message: "hello", user: user.id}) }}
.The side note i have is that the
twig:
prefix is to generic.So I started to look through the code and found the class that makes this work, github.com/symfony/ux-twig-compone...
It looks like a lot of work, but I didn't know you could do that. It is great I know this now, not sure if I ever going to need it.
On the topic of components. It has been popularized by front-end frameworks, but in css you had build tools like sass, less, and others that used the component idea already.
It is just an easy way to break down a UI.
You can use it from UX and design people to software developers, to create a consistent vocabulary.
I love to see how more back-end solutions are embracing the component idea. Like the Drupal single directory component, and the package you presented in your article.
Thank you for writing!
Hey, thanks for your feedback! I appreciate this deep answer! I didn't know about the Drupal single directory component thanks for sharing!