← All posts

Crafting Reusable Image Modules in FlyntWP with Twig and ACF

Crafting Reusable Image Modules in FlyntWP with Twig and ACF

When you’re building modern WordPress sites, the last thing you want is a slow, bloated experience, either for yourself as a developer or for your end-users. That’s precisely why I’ve embraced FlyntWP – it’s a lightweight, programmatic breath of fresh air compared to the sluggish low-code page builders that often compromise performance for convenience. FlyntWP empowers me to build exactly what I need, component by component, ensuring a clean DOM and exceptional Core Web Vitals. Today, I want to walk you through a specific technique: creating a truly reusable and performant image module using Twig and ACF Pro.

The goal is simple: an image component that can be dropped into any layout, accept various inputs, and render clean, semantic HTML without a pixel of unnecessary bloat. Here’s how we’d set it up.

First, let's define our fields in ACF Pro. We'll create a new field group, perhaps called "Image Module Fields," which we can then assign to our FlyntWP block or even use as a flexible content sub-field. Within this group, we’ll set up three key fields:

1. **Image Field (`image`):** This is an ACF Image field. Crucially, set the "Return Format" to "Image Array." This gives us access to all the image metadata in Twig, including URLs for different sizes, alt text, width, and height. 2. **Caption Field (`caption`):** A simple Text field. This allows content editors to add a descriptive caption below the image. 3. **Link Field (`link`):** A URL field. If populated, the image will be wrapped in an anchor tag, making it clickable.

With our ACF fields ready, we turn our attention to the Twig component itself. In FlyntWP, each "block" or "component" lives in its own directory, typically within `Components/`. Let’s assume we have a block named `ImageModule`. Inside `Components/ImageModule/index.twig`, we’ll write the rendering logic. This is where FlyntWP's modular approach shines, letting us craft beautiful, semantic markup directly.

Here’s what a stripped-down, yet powerful, `index.twig` for our ImageModule might look like:

{% raw %} ```twig {% if fields.image %} {% set image = fields.image %} {% set caption = fields.caption %} {% set link = fields.link %}

{% if link %} <a href="{{ link }}" class="image-module__link"> {% endif %}

<figure class="image-module"> <img src="{{ image.url }}" alt="{{ image.alt|default(caption)|striptags }}" loading="lazy" width="{{ image.width }}" height="{{ image.height }}" class="image-module__image" > {% if caption %} <figcaption class="image-module__caption">{{ caption }}</figcaption> {% endif %} </figure>

{% if link %} </a> {% endif %} {% endif %} ``` {% endraw %}

Let's break down this snippet. First, we check `{% if fields.image %}` to ensure an image has actually been selected. Then, we assign our ACF fields to local Twig variables (`image`, `caption`, `link`) for cleaner code. Notice how we conditionally render the `<a>` tag only if a `link` is provided. The `<img>` tag itself is where performance starts. We use `image.url`, `image.alt`, `image.width`, and `image.height` directly from the ACF image array. The `loading="lazy"` attribute is a quick win for Core Web Vitals, ensuring images off-screen don't block initial rendering. I also included a fallback for the `alt` text using `default(caption)|striptags`, which is a neat little trick to ensure accessibility even if the editor forgets the alt text. If a caption exists, it's rendered within a semantic `<figcaption>` tag inside a `<figure>`.

This approach gives us complete control. We’re not fighting against a page builder’s opinionated markup; we’re defining it ourselves. This means exceptionally clean DOM structures, which search engines love and users appreciate with snappy load times. We can easily extend this basic component to include more sophisticated features like `srcset` and `sizes` attributes for truly responsive images (something FlyntWP's Vite-powered build pipeline handles beautifully with modern asset bundling), or even integrate with a CDN. The modularity means this `ImageModule` can be reused across different block patterns, ensuring consistency and dramatically speeding up development. This isn't just about building faster; it's about building *better*.