This article originally appeared on our sister site, Symfony Station.
What’s the fix?
As the title of this article implies, developing for the frontend of the web can be a clusterfuck. I hope to help you make some sense of it in this in-depth article. And I mean in-depth.
Jonas Ulrich has a strong one if you want an alternative description of the problem.
My article grew from research. But not from intentional research about what this article will discuss. But rather from thinking about Mobile Atom Code’s client projects a year or two down the road. Is a decoupled approach to their web presence justified? And if so, what is the best way to go about it? Or progressively implement it. What is the best frontend solution?
Consequently, I explored various approaches to tying the frontend and backend of web projects together. Monolithic, decoupled, hybrid, micro-services, and API first? SPAs, MPAs, or PWAs?
This article will share what I have learned and my opinion on the best approach for various scenarios.
So before the JS bros start trolling, note that the article will be most applicable to web apps for smaller yet professional organizations. Small business, non-profit, and government spaces are examples. The same recommendations might not apply to large enterprise-level applications. And again, I am primarily covering web apps, not native apps.
Everything we will look at is about handling frontend interactivity. Menu and button animations, form submissions, on-click events, asset loading, etc., and the bug-a-boo of “state.”
But, before we continue, let’s look at some definitions because words are essential for intelligent people. I will let some people more intelligent than I define some of these.
SPAs - single-page apps
“A single-page application (SPA) is a web application or website that interacts with the user by dynamically rewriting the current web page with new data from the web server, instead of the default method of a web browser loading entire new pages. The goal is faster transitions that make the website feel like a native app.
MPAs - multiple page apps
ThirdRock Techkno defines them like this:
“a multi-page application (MPA) is an app that has more than one page. It works in a traditional way, requiring the app to reload entirely every time a user interacts with it.
MPAs generally have large data and complex architecture. The pages in MPAs mostly contain static content and links to go to other internal pages. This is why MPAs have a more intricate and multi-layered user interface.
They can be monolithic, progressive, or decoupled.
Monolithic apps - a united front and back end codebase
“In software engineering, a monolithic application describes a single-tiered software application in which the user interface and data access code are combined into a single program from a single platform.
A monolithic application is self-contained and independent from other computing applications. The design philosophy is that the application is responsible not just for a particular task, but can perform every step needed to complete a particular function.”
PWAs - progressive web apps
Via our friend Wiki:
Since a progressive web app is a type of webpage or website known as a web application, they do not require separate bundling or distribution. Developers can just publish the web application online, ensure that it meets baseline “installability requirements”, and users will be able to add the application to their home screen. Publishing the app to digital distribution systems like Apple App Store or Google Play is optional.”
Decoupled - headless is a stupid term
“Decoupled, or decoupling, is a state of an IT environment in which two or more systems somehow work or are connected without being directly connected.
In a decoupled microservices architecture, for example, software services have none or very little knowledge about the other services. In theory, this means that a change can be made to one service without the developer having to worry about how the change will impact other services -- as long as the service’s application programming interface (API) remains the same.
A decoupled architecture allows software development teams to build, execute, test and debug application modules independently. This approach also allows each module to be developed and maintained by a different team of software developers.”
I can handle this one. A library, as the name implies, is a set of components for the UI (User Interface) that impacts the UX (User Experience). They can be used in everything listed above.
Frameworks - Frontend and backend
“In computer programming, a software framework is an abstraction in which software, providing generic functionality, can be selectively changed by additional user-written code, thus providing application-specific software”, again according to Wikipedia.
OK! In a perfect world, here is where we’re heading.
Here’s a quick word about Native apps vs. web apps.
Would you like to write your app code once with standard web tech and then deploy it to multiple platforms? No Kotlin? No Swift? Or just a little of them to provide non-browser features if needed.
That’s a cross-platform app.
Recently, browser performance has drastically increased. So you can use the simpler languages you love and provide the experience users desire.
According to Chris Nielsen:
See the full I Replaced My Native iOS App with a Cross-Platform Web App and No One Noticed article for more.
You can program CPAs with a tool like Flutter. Or Chris recommends Ionic. Or, if you are a sadist, you could roll your own.
Also, native apps are closed source and corporate. However, web apps can be open-source.
So, we should create cross-platform apps if we create an “app store app”.
But, what we should be creating instead are PWAs.
PWAs - progressive web apps
“Progressive Web Apps (PWA) are web apps enriched via modern web standards with features considered typical for native apps. In addition to HTML5 APIs such as geolocation and webcam access, these include offline functionality and, for mobile devices, a prominent icon on the home screen.
PWAs have another big advantage: In principle, only one app needs to be developed that works on all platforms, since web browsers exist for every operating system. Instead of maintaining a separate app for each platform to be supported (usually written in a different programming language with completely different paradigms and patterns), there is now only one web app to serve them all – an extremely tempting offer, at least on paper.”
Standardized Web Components
There are many ways to develop your web components or blocks. Lit, Stencil, Lightning, and many more come to mind. Of course, as always, you could roll your own. But, it would be better if they were standardized and interchangeable for any application.
We are not there yet, unfortunately. But a movement is afoot on this front. And they are looking for developers to help.
Gutenberg and the Block Protocol are cooperating on one attempt that is underway.
They are working on “an open standard for building and using data-driven blocks. Make your applications both human and machine-readable.
Blocks built with the Block Protocol can easily pass data between applications because the data within each block is structured.”
So, again that’s a perfect world.
Of course, anything more than eleven humans are involved with is more likely to be a catastrophe than reach perfection. It’s especially true if challenging conditions exist like state.
The State Problem
State is described by W3.org:
“As defined by FOLDOC, state is how something is; its configuration, attributes, condition or information content. We use the term component to include software and hardware “things”. Virtually all components have state, from applications to operating systems to network layers. State is considered to be a point in some space of all possible states.”
“A component changes from one state to another over time when triggered by some kind of event. The event could be a network message, a timer expiring or an application message. Components that do not have state, that is there is no trigger that causes a transition, are called stateless.
Most interesting, particularly personalized, components have state of some kind, which is what allows them to provide personalized information when interacting with user agents on the web.
This finding concerns itself primarily with the following kinds of state:
- application state, which broadly is the state of a particular application;
- resource state, which is generally the state related to a resource identified with a URI.
- per user or per session state, which can cause a resource to interact differently according to the user making the access or the network connection on which the request is received.”
Nolan Lawson had done some hard thinking and great writing on “state”. And we have featured him in Symfony Station communiques. So, I will provide a lengthy quote from him that sums up the frontend problems and chaos this article examines in terms of state.
“Managing state is one of the hardest things about writing software. And in many ways state management is a great boon to SPAs. In particular, you don’t have to think about persisting state between navigations; it just happens automatically. In an MPA, you would have to serialize this state into some persistent format when the page unloads, and then rehydrate on page load.
On the other hand, the fact that the state never gets blown away is exactly what leads to memory leaks – a problem endemic to SPAs. Plus, the further that the state can veer from a known good initial value, the more likely you are to run into bugs.
Interestingly, though, it’s not always the case that an MPA navigation lands on a fresh state. As mentioned in a previous post, the back-forward cache (now implemented in all browsers) makes this discussion more nuanced.
In the past, I’ve tried to shed light on some interesting changes to MPAs that we might not have noticed. These changes are important, and may shift the calculus when trying to decide between an SPA or MPA architecture. And of course, frameworks are still innovating on both SPAs and MPAs.
The fact that SPAs neatly simplify so many aspects of application development – keeping state in one place, on the main thread, persistent across navigations – is one of their greatest strengths as well as a predictable wellspring of problems. Performance and accessibility wonks can continue harping on the problems of SPAs.”
State solutions may require decoupling for web apps.
Unfortunately, many people choose mainstream SPA frameworks to do it. And they compound the terrible decision by using them for their UI components. You can use web components within SPA frameworks. Ok.
SPA Frameworks, aka my Frontend Villains
But seriously, let’s explore frontend frameworks, wannabe frameworks, and why you should not use them for web apps.
I would compare Angular to Ultron, unnecessarily over-engineered, over-engineered again, and dangerous.
Angular is famous for being difficult and thus is falling out of favor.
It was developed by “don’t be evil,” but I am anyway Google.
I will readily admit I’m prejudiced against React. But, of course, I m also prejudiced against rednecks, religious fanatics, fascists, pedophiles, rapists, racists, conspiracy theorists, and stupid people in general.
React’s villain equivalent would be Penguin, overblown, prone to waddling, and incomprehensible. With an annoying accent.
Like Android, VHS, fast food, and pop music it sucks but is popular anyway. It was developed by a shit corporation, Facebook.
Its only redeeming quality is that it was forced into being open-source by WordPress. More on that will come below.
React itself is a component library, which would have been okay if challenging to use.
But to compete with real frameworks like Angular and Vue it has to add:
- React Hooks
- React Router
- React Query
- Context API
- Acid Reflux
- etc, etc.
In other words, it’s a clusterfuck inside a clusterfuck. And that leads to disastrous offspring. Imagine if the Penguin and Orca had a baby.
Like Loki, you have difficulty telling if VueJS is a hero or a villain. Like Angular and React, it is used when it shouldn’t be. But, if you insist on going all-in on a SPA framework, this is the one to use. Plus, more importantly, you can use it incrementally.
Vue has two versions that are incompatible with each other. So that sucks. But, hopefully, they learned their lesson and won’t repeat that mistake when it comes to new versions.
At least it’s genuinely open-source without the taint of Google or Facebook.
Plus, Batman and Loki could coexist if they had to.
Now, let’s finally examine how this horse-assery impacts PHP CMSs and backend frameworks.
It sucks because of “hydration.“
“Hydration is a solution to add interactivity to server-rendered HTML. This is how Wikipedia defines hydration:
The above definition talks about hydration in terms of attaching event handlers to the static HTML. However, attaching event handlers to the DOM is not the challenging or expensive part of the hydration, and so it misses the point of why anyone would call hydration an overhead. Overhead is work that can be avoided and still leads to the same end result.”
How can we ameliorate this unnecessary villainy? AKA, kick its ass.
First, Eliminate JS
Github has an excellent article about getting rid of frontend JS frameworks altogether. And this is what we should aim for when developing our web apps.
That’s fantastic news in the long run. And I will discuss some of these tools further below.
Note that MPAs can shine when it comes to eliminating JS.
But some aren’t. So if you can’t use the server-side tools solution you can use these first.
It’s a powerful tool for creating reactive applications using only PHP and HTML. It’s like a server-side template engine plus a frontend framework. You describe all components and logic in PHP, and then the tool transpiles them into native JS.
The project website viewi.net details the mechanism of operation and has code examples. And it’s pretty fast.
You can also follow a step-by-step tutorial and check out the Symfony integration. Great stuff.
Roll your own
Then, Minimize JS
Let’s examine some approaches that address hydration without the drawbacks of Angular, React, or Vue’s complexity.
Alpine’s creators say:
“Alpine is a powerful and lightweight tool for composing behavior directly in your markup. Think of it like jQuery for the modern web. Plop in a script tag and get going. You can do most of what you need to for JS with Alpine.”
It is a collection of 15 attributes, six properties, and two methods. That’s it.
Like other SPA frameworks, you can use it with almost anything.
The New Stack takes a solid look at it.
To quote Qwik: It is”
- General-purpose: Qwik is familiar for React developers and can be used to build any type of web site or application.
- Resumable: Qwik is resumable which means Qwik applications require 0 hydration. This allows Qwik apps to have instant-on interactivity.
- Progressive: Qwik takes full responsibility of how to load and download JS. No more manual code splitting.
- Reactive: Qwik semantics allow for fully reactive and efficient rendering.
- Fast: Qwik has unprecedented performance, offering sub-second full page loads even on mobile devices. Qwik achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
- Scalable: Qwik application have O(1) constant scalability. It does not matter if your application has 1 million components, boot time is unaffected.”
But, it’s a SPA, decoupled type solution in the making.
According to Astro:
In the PHP world, you can use it with WordPress.
Unpoly works with any backend framework and like SymfonyUX (a PHP take on Ruby’s Hotwire) sends HTML not JSON “over the wire”.
Its creator, Henning Koch describes Unpoly as:
The New Stack article linked just above says:
“Using Unpoly, your views can do things that are not normally possible in HTML, such as:
- Opening links in modal dialogs.
- Having links update only fragments of a page.
- Layer interactions.”
To quote Svelte:
“Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app.
Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.”
Like React and Vue, you can use it with almost anything. And I like it.
PHP CMSs / Platforms
Finally, how does this flood of approaches to the frontend impact my clients and maybe your projects?
Since we are in PHP land, I will examine the WordPress and Drupal content management systems, which are also the current solutions for my Mobile Atom Code clients.
Then their backend framework equivalents, Laravel and Symfony, will be explored. That covers the vast majority of PHP projects. All of this will apply to PWAs or regular websites.
First of all, with optimized hosting, caching, CDNs, and quality administration, most WP sites do not need a decoupled solution. So there is now no state problem. Wasn’t that easy?
For better or worse, usually the later WordPress’s JS ship has sailed. Unfortunately, WordPress chose React for its new content block-based ecosystem, with some rump PHP. The WP admin will remain PHP driven. The only good thing (I guess) is they forced Facebook into making React open-source by choosing it.
Fortunately, there are several ways to create blocks without writing them in React. So there is no need to sully your hands with working in it.
Find out more here if you are interested.
Sometimes you do need a decoupled solution to WordPress. In a competitive eCommerce situation, for example.
Almost Decoupled / Hybrid
WP Dev Academy has an approach they call “almost headless WordPress.” They say:
“Here’s the gist:
- The system uses AlpineJS for rendering. It’s light, fairly easy to understand, and it plays exceptionally nice with PHP server-side rendering.
- Most of the theme is loaded around HTML template tags. These tags get populated by WordPress’s REST responses for post content.
- The system makes judicious use of session storage. This drastically reduces the number of REST API calls, and keeps the site running fast.”
You saw the advantages of Svelte above. So if I went the hybrid route, I would use its approach.
Here’s a good video if you are interested.
By the way, I am working with a client now (summer 2022) using this solution.
Gatsby is used by many decoupled CMS sites. It’s an abstraction of React that is easier to use. And if I had to go the fully decoupled route, I would consider Gatsby over straight React, Angular, or Vue. Fortunately, I don’t. Because ⬇️.
All-inclusive Decoupled Hosting Solutions
If I had to go decoupled and had the budget, I would use a hosting solution that provides both front and backend technologies. Of course, like all decoupled solutions, there is double the hosting and double the coding, but at least it’s with one vendor.
Atlas is the decoupled solution from WP Engine. In addition, it includes FaustJS, which makes decoupled development easy by providing data fetching, authentication, and content previews from WordPress.
“By bringing WordPress into the world of headless, Atlas rescues content publishers from losing essential features such as post previews, SEO optimizations, layouts, and rich content creation.”
Strattic as the name implies, is a static WordPress solution.
“Strattic is an all-in-one static site generation and hosting platform that instantly optimizes WordPress by converting it to a static architecture. With Strattic, content managers and marketers can continue to manage content in WordPress as usual. At the same time, developers get to bask in the glory (and enjoy the peace of mind) of a fully headless website.”
I don’t know about glory. A stroked ego, yes.
Drupal is based on Symfony now and will remain a PHP-based CMS. Yay!
As with its competitor WordPress, most Drupal website projects do not need to be decoupled. It is especially true for the upcoming version 10 and its new default theme. However, again a quality hosting service, caching, a CDN, and top-notch admin optimization are required.
There are various ways to integrate web components into Drupal including Gutenberg blocks. We use them with Symfony Station. And yes, I know it’s based on React. That doesn’t mean I have to like it.
Here’s Lullabot’s approach to Component Libraries in Drupal to give you an idea of other solutions. Single File Components is an interesting one where "the module allows you to provide frontend components in a single file. These components contain Twig, CSS, JS, and PHP code which acts similar to a preprocess function." And Outline is a web component-based design system starter kit that also works with WordPress.
There are many Drupal modules dealing with decoupled and progressively decoupled frontend components. So, if interested, visit your favorite search engine.
In another DrupalCon presentation, John Locke noted:
“Drupal works extremely well as a backend headless CMS. But you don’t need to switch over to headless all at once. With some setup, you can start incorporating rich frontend elements on existing Drupal sites today!”
Progressive enhancement is the advantage of Vue and the best way to use it.
Alex Borsody followed up his presentation at DrupalCon with an article. Check out A Drupal developer's guide to Progressive Web Apps.
The same as for WordPress goes with Drupal.
Drupal can build more than websites, so you might need a decoupled solution. Cross-platforms apps? Definitely. eCommerce? Probably. Drupal calls itself a digital experience platform for a reason.
Also based on React, Drupal Next uses Next.js, as the name implies.
Its creator ChapterThree also wrote an article on How to Progressively Decouple your Drupal site with Next.js and the JSON API.
So it’s like Vue; it’s not all or nothing.
According to Tome’s creator:
“Tome is a static site generator, and a static storage system for content.
Long story short, you can use Drupal the same way you would use other static site generators like Jekyll or Hugo - everything lives in one repository, and Drupal only runs on your local machine.”
See Gatsby WordPress above. And find out more about using it with Drupal here.
All-inclusive Decoupled Hosting
Acquia, when speaking of their decoupled solution, says:
Of course, I have read and even edited posts about Laravel. But, I have little to no experience with it, so I’ll quote their solution’s creators.
Livewire is one of the approaches explored in the GitHub article mentioned above.
“Building modern web apps is hard. Tools like Vue and React are extremely powerful, but the complexity they add to a full-stack developer’s workflow is insane. Say hello to Livewire.
Livewire is a full-stack framework for Laravel that makes building dynamic interfaces simple without leaving the comfort of Laravel.
- Livewire renders the initial component output with the page (like a Blade include). This way, it’s SEO friendly.
- When an interaction occurs, Livewire makes an AJAX request to the server with the updated data.
- The server re-renders the component and responds with the new HTML.
- Livewire then intelligently mutates DOM according to the things that changed.”
If you follow us, subscribe to our emails, or frequently visit Symfony Station, you will have seen the Symfony solution for JS frontend conundrums.
And it’s genius.
It is an adaptation of the solution for Ruby on Rails examined in the GitHub article referenced earlier.
- Turbo Drive accelerates links and form submissions by negating the need for full page reloads.
- Turbo Frames decompose pages into independent contexts, which scope navigation and can be lazily loaded.
- Turbo Streams deliver page changes over WebSocket, SSE or in response to form submissions using just HTML and a set of CRUD-like actions.
- Turbo Native lets your majestic monolith form the center of your native iOS and Android apps, with seamless transitions between web and native sections.
It’s all done by sending HTML over the wire.”
Symfony’s version is Symfony UX and they say:
That’s Symfony UX.
Symfony UX Turbo is a Symfony bundle integrating the Hotwire Turbo library in Symfony applications.
Symfony UX Turbo also integrates with Symfony Mercure or any other transports to broadcast DOM changes to all currently connected users!
Symfony provides a straightforward component, built on top of the Mercure protocol.
Mercure is an open protocol designed from the ground up to publish updates from server to clients. It is a modern and efficient alternative to timer-based polling and to WebSocket.
Because it is built on top Server-Sent Events (SSE), Mercure is supported out of the box in modern browsers and has high-level implementations in many programming languages.”
Sulu - Decoupled
Sulu is the perfect full Symfony CMS to use with the frontend solution of your choice. If you are a Symfony developer and need to distribute to multiple platforms, it’s the way to go.
Ok. We’ve explored Frontend Madness with its morass of SPA, MPA, PWA, Decoupled, Hybrid, Monolithic, Library, and Framework solutions. What’s The Fix for your PHP backend?
So, after considering all I learned and my prejudice against React, here is the fix I recommend for your PHP backend web projects.
- Use a Hybrid / Progressively decoupled approach for a CMS with web components. Ditto for the infrastructure.
- For frameworks, use Symfony with Stimulus/Turbo/Mercure or Laravel with Livewire.
- Go the Hybrid / Progressively decoupled route with Svelte.
- If you must use a fully decoupled solution (suppose you are an eCommerce site where a slight difference in speed is crucial or you want a shopping app), choose Vue with a CMS or PHP framework backend.
I hope you will benefit from the results of my research for Mobile Atom Code projects. We’re sticking with the non-enterprise option 1 for now.
Thanks for reading my long and intricate article. Use the approaches discussed here in the manner that best fits your applications.
Some handy links are listed below for future reference.
Happy coding (with less JS) Symfonistas!
Resources / Courses
UI Frameworks / Web Components
Server Side Solutions
JS Frontend Libraries and Frameworks