Skip to main content
Building a Reusable Link Component with FlyntWP and ACF Pro

Building a Reusable Link Component with FlyntWP and ACF Pro

I’m a big believer in building WordPress sites that actually *perform*, not just look good.

I’m a big believer in building WordPress sites that actually perform, not just look good. That’s why I gravitated towards FlyntWP in the first place. Forget the sluggishness and div soup of conventional page builders; FlyntWP offers a programmatic, component-based approach that feels like a breath of fresh air. It leverages modern tools like Vite for blazing-fast development and, crucially, lets us craft our front-end with clean, modular Twig components. This focus on structured data and lean markup is exactly what we need to hit those crucial Core Web Vitals metrics, not just chase them.

One of the most powerful aspects of FlyntWP, for me, is the ability to create truly reusable components. Not just components that you drag and drop, but atomic pieces of UI logic that you can embed within other components, ensuring consistency and drastically reducing redundancy. Today, I want to walk through how I set up a robust, reusable “Link” component using ACF Pro’s Group field and Twig, demonstrating a pattern that can be applied to many other elements.

Think about how many times you need a button or a link on a page. In a traditional page builder, you might configure each one individually, or copy-paste a block, inevitably leading to inconsistencies or extra wrapper divs. With FlyntWP, we can define a single source of truth for our link data and its rendering logic.

My approach starts in ACF Pro. Instead of defining link_text, link_url, and link_target as separate sub-fields every time I need a link within a component like, say, a BlockHero or BlockCard, I create an ACF Group field. I’ll name this group link and give it sub-fields like text (Text field), url (URL field), target (Select field with _self and _blank options), and rel (Text field for nofollow, noopener etc.). The beauty of a Group field is that it’s a self-contained object. Now, when I’m building my BlockHero component’s ACF fields, I can simply add a Clone field and point it to my link group. This instantly pulls in all the link-related fields, guaranteeing consistency across every component that needs a link.

Next, on the Twig side, I create a dedicated partial, usually named _link.twig, located in a common Components folder within my theme, or within a specific elements folder if I want to keep my Components folder reserved for actual FlyntWP components. This partial is where the rendering magic happens. Inside _link.twig, I expect to receive a link object (the data from my ACF group) and potentially some additional options like a CSS class or an icon name.

Here’s the simplified logic I’d put in _link.twig:

{% if link.url and link.text %}
    <a href="{{ link.url }}"
       class="c-link {{ options.class }}"
       {% if link.target %}target="{{ link.target }}"{% endif %}
       {% if link.rel %}rel="{{ link.rel }}"{% endif %}>
        {{ link.text }}
        {% if options.icon %}
            <i class="icon-{{ options.icon }}"></i>
        {% endif %}
    </a>
{% endif %}

Now, within my BlockHero/index.twig file, when I need to render the hero’s main call-to-action link, I simply use include and pass the data:

{% include 'Components/_link.twig' with {
    link: fields.heroLink,
    options: { class: 'c-button c-button--primary' }
} %}

Notice how fields.heroLink directly corresponds to the link Group field I cloned in ACF. I’m also passing an options object to add specific styling classes for a button appearance. This allows the _link.twig partial to remain generic while still being highly customizable. I could even have a dedicated ComponentButton that only includes _link.twig with appropriate button classes, making it a standalone, draggable component that leverages the same underlying link logic.

This pattern isn’t just about reducing code. It’s about empowering FlyntWP to deliver on its promise of a superior developer and user experience. By centralizing our link definitions and rendering, we ensure every link on the site adheres to design guidelines, renders a clean and semantic DOM structure, and contributes positively to Core Web Vitals. Our Vite-powered build processes feel even snappier when components are this modular and efficient. We avoid unnecessary DOM complexity, which means smaller file sizes, faster render times, and a higher chance of a perfect Lighthouse score. It’s a small technique, but one that embodies the “programmatic, lightweight” ethos of FlyntWP beautifully.