← All posts

Crafting Truly Reusable Buttons with FlyntWP's Twig Components and ACF Pro

Crafting Truly Reusable Buttons with FlyntWP's Twig Components and ACF Pro

If you've spent any time building WordPress sites, you know the struggle: balancing client-friendly content management with a fast, lean, and maintainable codebase. Low-code page builders promise the world, but often deliver bloat, slow page loads, and HTML that looks like it was generated by a particularly confused robot. This is exactly why I’m such an advocate for FlyntWP. It's not just another theme starter; it's a philosophy built on structured content and blazing-fast performance. Today, I want to walk you through a core FlyntWP pattern that perfectly illustrates this: creating a truly reusable button component using Twig and ACF Pro.

Think about a common UI element: the humble button. On most sites, you’ll have buttons in heroes, calls to action, text blocks, footers – everywhere. If you're hardcoding button HTML directly into every component or relying on page builder modules that generate unique, slightly different markup each time, you're setting yourself up for a headache. Styles get inconsistent, accessibility becomes a nightmare, and if you ever need to change the base structure of a button, you're looking at a site-wide search-and-replace operation, praying you didn't miss anything. FlyntWP offers a superior solution through modular Twig partials.

Our goal is to define a button once, both in terms of its front-end markup and its backend ACF fields, and then reuse it consistently across our entire site. First, we'll create a dedicated Twig partial for our button. I like to prefix these reusable, standalone components with an underscore to denote they're not full "blocks" but rather sub-components or utilities. Let's create `Components/_Button/_Button.twig`:

<a href="{{ data.link.url }}" class="button button--{{ data.style }}"{% if data.link.target %} target="{{ data.link.target }}" rel="noopener noreferrer"{% endif %}> {{ data.link.title }} </a>

This simple snippet expects a `data` object with a `link` property (which will be an ACF link object) and a `style` property (e.g., 'primary', 'secondary'). Notice the `target="_blank"` and `rel="noopener noreferrer"` for external links – a good practice baked right into the component. The `button--{{ data.style }}` allows for easy CSS theming based on a selected style.

Next, we integrate with ACF Pro. In FlyntWP, you'd typically define your ACF field groups within the `Components` folder, specific to each block. However, for something as reusable as a button, we can define a *sub-field group* that can be nested within other components. Imagine an ACF field group named "Button Fields" with fields like: - `link` (Link field type) - `style` (Select field type, with choices like 'Primary', 'Secondary', 'Outline')

Now, when you create a larger component, say `BlockCallToAction`, you can simply add a "Group" field to its ACF definition and load your "Button Fields" within it. This keeps your ACF definitions DRY (Don't Repeat Yourself) and ensures consistency across all button instances.

Finally, we put it all together. Let's say we have a `BlockCallToAction/index.twig` file for a component that displays some text and a button. Instead of writing out the button HTML directly, we simply include our `_Button` partial, passing it the necessary data from the parent component's ACF fields:

<div class="blockCallToAction"> <div class="wrapper"> <h2>{{ data.title }}</h2> <p>{{ data.content }}</p>

{% include 'Components/_Button/_Button.twig' with { data: { link: data.button.link, style: data.button.style } } only %} </div> </div>

In this `BlockCallToAction` example, `data.button` would be the group field from ACF that contains our `link` and `style` fields. We pass `data.button` to the `_Button` partial's `data` variable. The `only` keyword ensures that only the explicitly passed variables are available to the included template, preventing accidental variable leakage and keeping things clean.

This approach offers immense benefits. First, it's incredibly modular. Any change to the `_Button.twig` file instantly updates every button across the site. Second, it enforces design consistency. No more slightly different button markup or classes appearing across pages. Third, the client experience in the backend is streamlined: they fill out intuitive ACF fields for the button, not wrestling with HTML. From a performance perspective, you get a consistently clean, minimal DOM structure, which is a key factor in achieving those flawless Core Web Vitals scores. With FlyntWP's Vite integration handling the assets, these lean components load almost instantaneously. It's a pragmatic, developer-friendly way to build fast, beautiful, and maintainable WordPress sites, truly separating content from presentation without the bloat.