← All posts

FlyntWP Tip: Building a Flexible Button Component with Twig & ACF

FlyntWP Tip: Building a Flexible Button Component with Twig & ACF

I've been working with FlyntWP for a while now, and it constantly reminds me why I made the switch from heavy, drag-and-drop page builders. If you're tired of battling sluggish site speeds, bloated DOMs, and an endless array of divs just to style a simple element, then FlyntWP's philosophy of lightweight, programmatic development with modular Twig components and ACF Pro integration is truly refreshing. It's about taking back control and building precisely what you need, with performance and maintainability baked in from the start. Today, I want to walk you through a practical example of this power: creating a truly flexible and reusable button component that you can deploy anywhere on your site, ensuring flawless Core Web Vitals and lightning-fast Vite build speeds.

One of the common frustrations with low-code builders is how difficult it is to create simple, reusable elements without generating excessive markup. A button, for instance, often ends up wrapped in several unnecessary parent divs, each contributing to render-blocking CSS and a higher DOM depth. In FlyntWP, we approach this differently. We define our components as small, self-contained units using Twig for markup and ACF Pro for data input. This means our button component will be pure, clean HTML, and its data will be easily manageable by content editors.

Let's dive into setting up our `ComponentButton`. First, we need to define the ACF fields that will power it. In your `components/ComponentButton/functions.php`, you'd register a field group like this:

add_filter('Flynt/addComponentData?name=ComponentButton', function ($data) { $data['fields'] = [ [ 'label' => 'Button', 'name' => 'button', 'type' => 'group', 'sub_fields' => [ [ 'label' => 'Link', 'name' => 'link', 'type' => 'link', 'return_format' => 'array', ], [ 'label' => 'Style', 'name' => 'style', 'type' => 'select', 'choices' => [ 'primary' => 'Primary', 'secondary' => 'Secondary', 'outline' => 'Outline', ], 'default_value' => 'primary', 'wrapper' => [ 'width' => '50', ], ], ] ] ]; return $data; });

This ACF group gives us a standard WordPress link picker for the URL and text, and a select field for a `style` class, all nested under a 'button' group. Now, for the magic in `components/ComponentButton/index.twig`:

{% set link = fields.button.link %} {% set style = fields.button.style ? 'button--' ~ fields.button.style : 'button--primary' %}

{% if link.url and link.title %} <a href="{{ link.url }}" class="button {{ style }}" target="{{ link.target ?: '_self' }}" rel="{{ link.target == '_blank' ? 'noopener noreferrer' : '' }}"> {{ link.title }} </a> {% endif %}

Notice how clean that Twig template is. It directly outputs an `<a>` tag with minimal fuss, dynamically adding classes based on our ACF selection. No extraneous divs, no complex nested structures. This is a crucial aspect of why FlyntWP sites consistently achieve excellent Core Web Vitals scores: the DOM is lean, meaning browsers parse and render pages faster.

The real power, however, lies in reusability. Instead of duplicating this code or setting up a new ACF field group every time you need a button, you can now *include* `ComponentButton` in other components and pass data to it. For example, if you have a `ComponentHero` and you want to add one or more buttons, your `ComponentHero/index.twig` might look something like this:

<section class="hero"> <div class="hero__content"> <h1>{{ fields.headline }}</h1> <p>{{ fields.content }}</p> {% if fields.buttons %} <div class="hero__actions"> {% for buttonData in fields.buttons %} {% include 'Partials/_ComponentButton.twig' with { fields: { button: buttonData.button } } %} {% endfor %} </div> {% endif %} </div> </section>

Here, `fields.buttons` would be an ACF Repeater field in your `ComponentHero`'s `functions.php`, where each row includes the sub-fields from our `ComponentButton`'s 'button' group (link, style). Crucially, your `Partials/_ComponentButton.twig` is simply `{% include 'components/ComponentButton/index.twig' %}`. By using `include 'Partials/_ComponentButton.twig' with { fields: { button: buttonData.button } }`, we're telling Twig to render our `ComponentButton` using the specific data from *that repeater row*. This ensures the `ComponentButton` always expects its data in the same `fields.button` structure, regardless of where it's included, making it incredibly powerful for maintaining consistency and speeding up development.

This pattern exemplifies FlyntWP's strengths: programmatic control over markup, robust content management with ACF Pro, and a commitment to performance. With Vite handling asset compilation, your development workflow is blazing fast, giving instant feedback on changes. This approach not only results in faster websites with beautiful, clean DOM structures but also a more enjoyable and efficient development process for you. Give it a try; you'll appreciate the difference.