If there was Astro for WordPress

Manos Menexis
Posted by Manos Menexis

8 minutes read

Most developers will try to build their own tools and project boilerplates at some time in their career. And so did I, aiming for my ideal dev setup with bleeding edge technologies. This is my story of the struggles I faced along the way and how Flynt became my default choice for developing WordPress websites.


The excitement typically revolves around the ever-evolving revolutionary technologies, the latest shiny frameworks, or the emergence of new programming languages. While these advancements push web development forward, I believe it’s important, from time to time, to recognise the substantial progress we’ve made and appreciate the well-established tools we frequently rely on everyday. One of those tools for me is WordPress.

My web development journey

WordPress was one of my first experiences with Web Development, back when I used to be a designer, and I have to say I had a rollercoaster of feelings when I started using it. As I was inexperienced with code I had to leverage the premium WordPress theme ecosystem to create my websites. Some themes were quite powerful and with a lot of options, although they usually came with a ton of extra features the I would rarely use. That lead to performance issues and from time to time security vulnerabilities as well. At the time I was focused on learning more about CSS and HTML as I didn’t want to overwhelm myself with too much at one time.

After a while, I ended up working on a startup as a designer, where we were developing a pretty complex web app that included, among other things, a real time global chat, a ranking system, used for matchmaking users based on their performance, listing and trading of digital and physical products between users and the platform, leader boards and the list goes on. When we decided to do a re-write of the app, I get involved with the frontend and tried to learn more about web development. That was my first introduction to more advance tooling like bundlers package managers etc.

Creating my own boilerplate for WordPress

With that knowledge in hand I decided to create my own WordPress theme boilerplate that would include all of those technologies. My goal was to have a starting point for each WordPress project that I would develop in the future. While this might sound easy on paper, getting everything right was a long process of using what I created and realising that I had to structure something differently, fix a bug etc. I ended up focusing more on maintaining and improving the boilerplate than actually working on the projects. This made me look for other solutions and at that time the static generators were the new shiny thing.

The rise of static site generators

I started following the web development world when a lot of the rendering was moving from the server to the client and frameworks like React, Vue and Angular became popular. While they are great solutions for creating dynamic web applications, when it comes to simple SEO friendly websites nothing can beat static. Hopefully static site generators where developed to translate the JavaScript code to static HTML. This was, for a while, the way I created websites for my projects. using tools like Gatsby in the beginning and moving to Astro eventually.

Lately though, it seems like the path was not a straight line but more of a circle. It started from the server moved to the client and now we are moving back to the server. I am personally very exited about these cycles because I think it tests the limits of what our current technologies can do, while promoting innovation that make the web a better more inclusive space for everyone.

And then I was introduced to Flynt

I learned about Flynt just before I joined Bleech. At first it looked strange to me, as I hadn’t heard of Timber or Twig before, but when I started using it it really made sense. Flynt is basically a WordPress Theme Starter, although if you get to know it, it is way more powerful than that. The current version of Flynt was 1.4 at the time, and It had already solved CSS and Javascript scoping, responsive images and most importantly developer experience. Then version 2.0 came out with a lot of improvements and new features inspired by the modern development ecosystem.

How does it work?

The basic idea of Flynt is that you create reusable component to construct your layouts and content. Some components can be used to create content and others serve as common elements among the website, like the header and footer for example. The anatomy of a component looks like this:

An image showing a flynt component on a mac finder window

  • index.twig This is the html template
  • _style.scss Scoped styles for the frontend
  • functions.php Can be used to run server code, register custom fields, translatable content or modify the data of the component
  • script.js Using web components under the hood to scope styles and lazy load the Javascript code.
  • README.md Used to provide documentation
  • screenshot.png Used as a visual reference both on the codebase as well as the WordPress admin.
  • _admin.scss / admin.js Used to add styles and Javascript on the WordPress admin.

As you can see the structure looks similar to what you would find in other popular frameworks. The component will be available to use inside your codebase as soon as you create some or all of these files. There is an extra step though if you want to use it as part of the content for posts or pages for example, and that is registering it in the appropriate file found under the inc/fieldGroups.

My favourite features

Some of the coolest features for me are the concept of islands architecture, a reference system to access HTML elements and a solid way to communicate data between server and client using simple json scripts. Let’s take a look on how those features look like

1. Islands architecture

Different loading strategies can be defined for each component independently when using the custom element flynt-component:

  • load:on="load"  (default) Initialises immediately when the page loads. Usage example: Elements that need to be interactive as soon as possible.
  • load:on="idle" Initialises after full page load, when the browser enters idle state. Usage example: Elements that don’t need to be interactive immediately.
  • load:on="visible" Initialises after the element get visible in the viewport.Usage example: Elements that go “below the fold” or if you want to load it when the user sees it.
  • load:on:media="(min-width: 1024px)" Initialises when the specified media query matches.Usage example: Elements which may only be visible on certain screen sizes.

This allows you to control how javascript is loaded and, when used correctly, can lead to a great balance between user experience and performance.

2. Reference system

The reference system provides a way to access DOM elements with the buildRefs functions. It requires that you provide a data-ref attribute for single selection or data-refs for multiple, with a name as the value, data-ref=”alertToggle” for example. What this function does under the hood is:

  • When you call the buildRefs functions a new proxy is created that stores a object with the ref name and the corresponding element.
  • Whenever you access a ref the functions checks if the property already exists on the target object.
  • If not, it creates a selector string either from customRefs or constructs one using a querySelector of querySelectorAll and the property name.
  • It then attempts to find the element(s) in the DOM
  • If no element(s) are found and the environment is not production, it logs a warning to the console.
  • Finally, it returns the found element(s) or null if none were found.

Lets take a look on how this works:

For the HTML I will just create a simple button and give it the data-ref=”alertToggle” attribute.

<flynt-component name="BlockAlert">
  <button data-ref="alertToggle">Alert me!</button>

Then inside the component script I will use the function buildRefs to access it:

import { buildRefs } from '@/assets/scripts/helpers.js'

export default function (el) {
  const ref = buildRefs(el)
  ref.alertToggle.addEventListener("click", () => alert("I am a ref called alertToggle"))

3. Server – Client Sync

There is also a helper function called getJSON. This function will, by default, retrieve the content of a script with type “application/json” from the components markup. This can be useful if you want to pass data from the server to the client in a json format. I find this very useful when I want to persist state from the server to the client on a page reload for example.

Here is an example on how this would look like. First on our functions php lets create some data:

add_filter('Flynt/addComponentData?name=BlockServerSynch', function ($data) {
    $data['jsonData'] = [
        "foo" => "bar"

    return $data;

Then on the twig file we add the scrip with our data.

<flynt-component name="BlockServerSynch">
  <script type="application/json">
    {{ jsonData|json_encode }}

And then we can grab the data from the server inside the script file:

import { getJSON } from '@/assets/scripts/helpers.js'

export default function (el) {
  const data = getJSON(el)

I have used this in the past to pass filters, user inputs, translatable string or user options among other things and I really like how simple and to the point it always feels.


As someone that really likes to balance between the bleeding edge and production ready solutions I find that Flynt has become my go to starter theme for WordPress development. It enables me to write maintainable code faster, while it takes care of performance, scoping and reusability in a very robust way. As an official member of the team that develops and maintains Flynt now, I see all the thought and effort that is put into it and I can’t wait to see what new features are coming.

But don’t take my word for it, go and try it yourself! If you need any help you can always start a discussion on Github or reach out on 𝕏, we are always happy to help.

More Posts

We help your WordPress website and business stay ahead of the curve.

Contact Us