In my first blog post, I introduced the Custom Elements module: A solution for soft-decoupled Drupal! Following that, I want to put more emphasize on the frontend side of the stack in this blog post:
Selecting a frontend framework
With the backend producing custom elements markup, we need a frontend framework that is able to work with that. While there are many frameworks that play nice with custom elements, we want to pick a well-established solution with a large user base. That way, it's more likely that the framework stays relevant and well maintained on the long term. Also, finding more integrated libraries, learning materials or developers knowing the framework becomes much easier.
According to The State of JavaScript 2019: Front End Frameworks the top 3 frameworks developers have used are React, Vue.js and Angular:
Angular has been not liked much by developers that used it and won't be used again by the majority of them (see survey). Besides that, it's provided as a whole MVC framework with lots of things potentially not needed here. So not the best fit.
React has a very strong user base, but does not play that well with custom elements. While there are solutions like react-slot, they are not as common and well maintained. Finally, personally I don't like the "Everything is Javascript" approach so much.
Vue.js on the other hand, comes with template parsing built-in that is able to render custom elements data easily. Like React, it utilizes a virtual DOM and is well adopted and continuously growing. Finally, the Vue.js single file components are following a template based, web-standard oriented approach.
Since web components build upon custom elements, they seem to be the perfect fit. However, they are not, since they are not well adopted and server-side rendering web components is not a well solved issue (yet?).
Vue.js
Thus, Vue.js turns out to be the ideal candidate for our decoupled Drupal stack built upon custom elements. Moreover, I like it for the following:
It's approachable and easy to use. It has great documentation and sticks to web standard oriented notations.
It comes with a set of incrementable adoptable companion libraries for further needs, like routing or state management.
It's fast! It only weighs 20kB gzipped and scores well in rendering benchmarks.
Just like Drupal, it's a community backed open-source project, not depending on a single large corporation. Check out the Vue.js team and its sponsors.
Nuxt.js
Once one decides for Vue.js, one quickly stumbles over Nuxt.js - the intuitive Vue framework. It comes with a set of useful conventions - inspirated by Next.js - that make it very easy and enjoyable to get started with development. It provides all necessary setup for Javascript transpilation, CSS processing and improves performance by handling route-based automatic code-spliting or link prefetching. Thankfully, it's following Vue.js principles, thus it emphasizes the ease of use and provides great documentation.
Finally, it's built upon a powerful, modular architecture and allows providing features as re-usable Nuxt.js modules - what makes it a great addition to Drupal. While the number of modules is nowhere comparable to Drupal, there are many useful modules available, like PWA generation, Google Analytics or Tag manager integration, or the usual frontend/CSS framework integrations with Bootstrap, Material UI or Tailwind CSS. Check out the module directory at modules.nuxtjs.org for more.
Nuxt.js deployment options
One of the nice features of Nuxt.js is that it's really easy to deploy your project using various ways, all from the same code-base - just by a different deployment configuration. The options are:
Static generation
Generate a static site and leverage the Jamstack approach for dynamic functionality and content previews.Server Side Rendering
Render the markup using a Node.js server or Serverless functions.Single Page Application
Just serve a pre-built Javascript application to users and let the user's browser take over rendering.
That way, one can choose the best suiting deployment target per project. Small sites without a large number of pages could be very easily statically generated and benefit from fast, cheap and secure hosting; e.g. via providers like Netlify, Amazon S3 or Github pages. If SEO is not required, running as a Single Page application does away the need for re-building after content changes and allows for highly dynamic, user-specific content and app-like UIs.
More demanding projects, requiring SEO and having a large number of content, can use server side rendering with any Node.js compatible hosting provider like Platform.sh or Heroku. Alternative options would be specialized hosting providers like Vercel, AWS amplify or deploying via the Serverless framework to various serverless cloud providers.
The frontproxy approach
Finally, I'd like to mention we also developed a custom, more advanced approach that becomes possible with custom elements markup: The lupus-frontproxy is a small PHP-based script that serves content as custom element markup combined with a pre-generated app shell (site header and footer). That way, large, content-heavy sites can easily run without a node.js server driving the frontend, while still providing decent SEO based upon the custom element markup delivered (Google just ignores custom elements and indexes the contained markup). However, with the rise of easy and affordable hosting options for server side rendering, such a custom built, uncommon solution seems unnecessary and not really worth the efforts.
Summing up
Nuxt.js is a great framework that makes it really easy to build a Vue.js based frontend application that renders the custom elements markup provided by the Drupal backend. Since each custom element is mapped to a Vue.js component, it's the perfect fit for building up component-based frontends, while keeping the editorial controls in the backend.
Thanks to its flexible deployments options, we can leverage static generation for smaller sites and use server-side rendering for larger, more content-heavy sites.
Following up
Since there are so many details more to talk about, I'll be following up with further blog posts in the coming weeks, covering the following topics:
Authentication & related architecture variants. Custom Elements markup & json formats.
A developer introduction: Creating components, adding Custom Element processors, the relationship to Drupal's render API, Custom routes and optimizing cache metadata.
Handling blocks & layout builder, content previews, forms, caching & performance optimizations.
Finally, I'm going to talk more about this stack at the Drupalcon Europe 2020 in my session "Custom Elements: An alternate Render API for decoupled Drupal" at December 08 - 09:15 - so mark your calendars!